<?php

namespace App\Kuafu\Shop\Service;

use App\Exceptions\BaseException;
use App\Kuafu\Audit\Enum\AuditEventCodeEnum;
use App\Kuafu\Audit\Service\AuditService;
use App\Kuafu\Base\Enum\BaseCodeEnum;
use App\Kuafu\Base\Enum\DBEnum;
use App\Kuafu\Base\Service\BaseService;
use App\Kuafu\Common\Enum\CodeEnum;
use App\Kuafu\Common\Service\CommonService;
use App\Kuafu\Common\Service\InformationPushService;
use App\Kuafu\Config\Model\ConfigModel;
use App\Kuafu\Config\Service\ConfigService;
use App\Kuafu\Customer\Enum\PlatformCodeEnum;
use App\Kuafu\Customer\Service\CustomerPlatformService;
use App\Kuafu\Mqtt\Model\Log;
use App\Kuafu\Mqtt\Service\MqttQueueService;
use App\Kuafu\Mqtt\Service\MqttService;
use App\Kuafu\Nsq\Enum\NsqCodeEnum;
use App\Kuafu\Order\Enum\OrderCodeEnum;
use App\Kuafu\Order\Exceptions\OrderException;
use App\Kuafu\Order\Service\OrderPayService;
use App\Kuafu\Pay\Channel\FuYou\FuYouBase;
use App\Kuafu\Pay\Model\PayStreamModel;
use App\Kuafu\Product\Enum\ProductCodeEnum;
use App\Kuafu\Product\Exceptions\ProductException;
use App\Kuafu\Product\Model\Product;
use App\Kuafu\ProductMenu\Enum\ProductMenuCodeEnum;
use App\Kuafu\ProductMenu\Exceptions\ProductMenuException;
use App\Kuafu\ProductMenu\Model\ProductMenu;
use App\Kuafu\Shop\Enum\AreaCodeEnum;
use App\Kuafu\Shop\Enum\CodeEnum as ShopEnum;
use App\Kuafu\Shop\Enum\ShopCacheEnum;
use App\Kuafu\Shop\Enum\ShopCodeEnum;
use App\Kuafu\Shop\Exceptions\ShopException;
use App\Kuafu\Shop\Model\AreaModel;
use App\Kuafu\Shop\Model\CameraModel;
use App\Kuafu\Shop\Model\CollectShopListModel;
use App\Kuafu\Shop\Model\Shop;
use App\Kuafu\Shop\Model\ShopArchives;
use App\Kuafu\Shop\Model\ShopOperateTargetModel;
use App\Kuafu\Shop\Model\ShopPaymentsModel;
use App\Kuafu\Shop\Model\ShopProductStatus;
use App\Kuafu\Shop\Model\ShopStaffRecordModel;
use App\Kuafu\Shop\Resources\merchant\CityShopListResource;
use App\Kuafu\Shop\Resources\merchant\ShopListResource;
use App\Kuafu\Shop\Service\DouyinSelfLibrary\DouyinSelfService;
use App\Kuafu\Shop\Validate\ShopValidate;
use App\Kuafu\Sys\Model\Menus;
use App\Kuafu\User\Enum\CodeEnum as UserCodeEnum;
use App\Kuafu\User\Enum\RoleCodeEnum;
use App\Kuafu\User\Exceptions\UserException;
use App\Kuafu\User\Exceptions\UserRoleException;
use App\Kuafu\User\Model\Role;
use App\Kuafu\User\Model\RolePermissionMappings;
use App\Kuafu\User\Model\User;
use App\Kuafu\User\Model\UserRoleMapping;
use App\Kuafu\User\Service\AuthService;
use App\Kuafu\User\Service\UserRoleService;
use App\Kuafu\User\Service\UserService;
use App\Kuafu\WaiMai\Enum\WaiMaiDBEnum;
use App\Kuafu\WaiMai\Model\WaiMaiShopMappingModel;
use App\Kuafu\WaiMai\Service\WaiMaiShopService;
use App\Library\FeiShu\SendFs;
use App\Library\KfHttp\TigerCurl;
use App\Library\Redis\RedisHelp;
use App\Library\TencentMap\TencentMapHelp;
use App\Remote\Tiger\OpenShop\OpenShopRemote;
use App\Remote\Tiger\OrganizationManage\OrganizationManageRemote;
use App\Remote\XiaoHongShu\XhsShopRemote;
use App\Support\MQTT;
use Carbon\Carbon;
use Doctrine\DBAL\Exception;
use GuzzleHttp\Exception\GuzzleException;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
use Illuminate\Support\Facades\Schema;
use Kuafu\File\FileUtil;
use Kuafu\File\PrivateFileUtil;
use OSS\Core\OssException;
use Psr\SimpleCache\InvalidArgumentException;


class ShopService extends BaseService
{
    /**
     * 添加店铺
     * @param $request
     * @param bool $checkRegion 强制校验地区
     * @return int
     * @throws ShopException
     */
    public static function addShop($request, bool $checkRegion = true): int
    {
        $status = (int)$request->get('status', ShopEnum::SHOP_STATUS_NORMAL);

        // 门店编号不传，自动用生成器
        if (!$request->shopCode) {
            $request->shopCode = (new ShopCodeService())->createShopCode();
        }

        $timeChange = self::shopBusinessTimeChange($request->startAt, $request->endAt);

        $detailAddress = AreaModel::getAreaDetail($request->province, $request->city, $request->district);
        if (empty($detailAddress) || empty($detailAddress->cityRel) || empty($detailAddress->cityRel->distinctRel)) {
            if ($checkRegion) {
                throw new ShopException(ShopCodeEnum::ERR_AREA_REL);
            } elseif ($request->get('adRegionCode')) {
                SendFs::newRobot()
                      ->setTitleContent('新增店铺时，地区校验失败需要手动同步',
                          json_encode($request->all(), JSON_UNESCAPED_UNICODE))
                      ->send();
            }

        }
        $addressPrefix = vsprintf('%s-%s-%s', [
            $detailAddress->cnShortName ?? '',
            $detailAddress->cityRel->cnShortName ?? '',
            $detailAddress->cityRel->distinctRel->cnShortName ?? ''
        ]);

        $shop = Shop::addData(
            $request->name,
            $request->shopCode,
            $request->shopMobile,
            $request->type,
            $addressPrefix,
            $request->address,
            $request->brandId,
            $timeChange['startAt'],
            $timeChange['endAt'],
            $request->province,
            $request->city,
            $request->district,
            $request->lng,
            $request->lat,
            $request->branch,
            str_replace(config('other.IMAGE_URL'), '', $request->privateDrainageImg),
            str_replace(config('other.IMAGE_URL'), '', $request->orderingQrCode),
            empty($request->formalUseAt) ? 0 : strtotime($request->formalUseAt . ' 00:00:00'),
            $status
        );

        ShopService::delSelectionCache();

        (new \Kuafu\Nsq\NsqPublisher(config('nsq.default.address')))
            ->pub(NsqCodeEnum::SHOP_CREATE, ['shop' => $shop->toArray()]);

        // 新建门店初始化门店属性
        ShopAttributeService::createShopAttribute($shop->toArray());
        return $shop->id;
    }

    /**
     * 店铺详情
     * @param int $shopId
     * @return mixed
     */
    public static function getShopInfo(int $shopId)
    {
        return Cache::remember(ShopCacheEnum::getShopInfo($shopId), getSwitchCacheTime(3600),
            function () use ($shopId) {
                return Shop::query()->find($shopId);
            });
    }

    /**
     * 通过 ShopCode 获取门店详情
     *
     * @param int $shopCode
     * @return Builder|\Illuminate\Database\Eloquent\Model|object|null
     */
    public static function getShopInfoByCode(int $shopCode)
    {
        return Shop::query()->where('shopCode', $shopCode)->first();
    }

    /**
     * 获取门店所有状态列表
     *
     * @param $shopId
     * @return array
     */
    public static function getShopAllStatus($shopId): array
    {
        $shopInfo = ShopService::getShopInfo($shopId);
        if (!empty($shopInfo)) {
            return [
                'status'             => $shopInfo->status,
                'isBusiness'         => $shopInfo->isBusiness,
                'appletStatus'       => $shopInfo->appletStatus,
                'cashRegisterStatus' => $shopInfo->cashRegisterStatus,
            ];
        } else {
            return [];
        }
    }

    public static function getShopInfoShow(int $shopId): ?array
    {
        $info       = self::getShopInfo($shopId);
        $returnData = null;
        if ($info) {
            $shopBusinessTimeChangeByDb = ShopService::getShopBusinessTimeChangeByDb($info['startAt'], $info['endAt']);
            $addressPrefix              = explode('-', $info['addressPrefix']);
            $formatAddressPrefix        = (count($addressPrefix) == 3 ? true : false);
            $returnData                 = [
                'id'                 => $info['id'],
                'shopCode'           => str_pad($info['shopCode'], ShopCodeEnum::CODE_TEXT[ShopCodeEnum::LEADING_ZERO],
                    "0",
                    STR_PAD_LEFT),
                'shopMobile'         => $info['shopMobile'],
                'shopContact'        => $info['shopContact'],
                'name'               => $info['name'],
                'shortName'          => ShopService::getShortName($info->toArray()),// 门店简称
                'type'               => $info['type'],
//                'imageUrl'  => env("IMAGE_URL").$info['imageUrl'],
                'status'             => $info['status'],
                'cashRegisterStatus' => $info['cashRegisterStatus'],
                'address'            => $info['address'],
                'createdAt'          => $info['createdAt'],
                'startAt'            => $shopBusinessTimeChangeByDb['startAt'],
                'endAt'              => $shopBusinessTimeChangeByDb['endAt'],
                'serialNumber'       => $info['serialNumber'],
                'lng'                => $info['lng'],
                'lat'                => $info['lat'],
                'province'           => $info['province'],
                'city'               => $info['city'],
                'district'           => $info['district'],
                'branch'             => $info['branch'],
                'cameraSupplierId'   => $info['cameraSupplierId'],
                'cameraSupplier'     => $info['cameraSupplier'],
                'provinceText'       => $formatAddressPrefix ? $addressPrefix[0] : '',
                'cityText'           => $formatAddressPrefix ? $addressPrefix[1] : '',
                'districtText'       => $formatAddressPrefix ? $addressPrefix[2] : '',
                'privateDrainageImg' => $info['privateDrainageImg'] ? CommonService::joinImageUrl($info['privateDrainageImg']) : '',
                'orderingQrCode'     => $info['orderingQrCode'] ? CommonService::joinImageUrl($info['orderingQrCode']) : '',
                'formalUseAt'        => $info['formalUseAt'] > 0 ? date('Y-m-d', $info['formalUseAt']) : '',
                'isInvoice'          => empty(ShopInvoiceService::getShopInvoiceInfo($shopId)) ? false : true,
                'menuId'             => $info['menuId'],
                'menuName'           => ProductMenu::query()->select('name')->where('id', $info['menuId'])
                                                   ->value('name') ?? '',
                'councilorId'        => $info['councilorId'],
                'councilorName'      => $info['councilorId'] == 0 ? '' :
                    attributeGet(User::query()->find($info['councilorId']), "username", "")
            ];
        }
        return $returnData;
    }

    public static function getShopListByBrandId($brandId, $pageSize, $pageNum, $status, $name, $field): array
    {
        $query = Shop::getShopListByBrandPage($brandId, $pageSize, $pageNum, $status, $name, $field);
        return [
            'pageSize' => $query->perPage(),
            'pageNum'  => $query->currentPage(),
            'count'    => $query->total(),
            'lists'    => $query->items(),
        ];

    }

    public static function getShopList($request)
    {
        try {
            //获取用户id
            $userId = AuthService::getUserId();
            //查询品牌下的店铺ids
            $shopIdListReturn = UserRoleService::getUserShopIdsList($userId, $request->header('brandId'));
            //查询店铺详情
            return self::getShopListByIds($shopIdListReturn);
        } catch (\Exception $exception) {
            throw new ShopException(ShopCodeEnum::ERR_DB_ERROR, '查询店铺列表失败');
        }
    }

    /**
     * 根据品牌ids 获取品牌详情
     *
     * @param array $idList
     * @return array
     */
    public static function getShopListByIds(array $idList, array $condition = []): array
    {
        $shopList = Shop::query()
                        ->when(isset($condition['status']) && !is_array($condition['status']),
                            function ($query) use ($condition) {
                                $query->where('status', $condition['status']);
                            })
                        ->when(isset($condition['status']) && is_array($condition['status']),
                            function ($query) use ($condition) {
                                $query->whereIn('status', $condition['status']);
                            })
                        ->find($idList, [
                            'id',
                            'shopCode',
                            'name',
                            'brandId',
                            'imageUrl',
                            'address',
                            'type',
                            'status',
                            'updatedByUid',
                            'createdByUid',
                            'serialNumber',
                        ]);
        if (empty($shopList)) {
            return [];
        }

        return $shopList->toArray();
    }

    /**
     * 编辑店铺信息
     * @param $request
     * @return array
     */
    public static function setShopInfo($request)
    {
        try {
            $timeChange    = self::shopBusinessTimeChange($request->startAt, $request->endAt);
            $detailAddress = AreaModel::getAreaDetail($request->province, $request->city, $request->district);
            if (empty($detailAddress) || empty($detailAddress->cityRel) || empty($detailAddress->cityRel->distinctRel)) {
                throw new ShopException(ShopCodeEnum::ERR_AREA_REL);
            }
            $addressPrefix = vsprintf('%s-%s-%s', [
                $detailAddress->cnShortName,
                $detailAddress->cityRel->cnShortName,
                $detailAddress->cityRel->distinctRel->cnShortName
            ]);
            $data = [
                "name"               => $request->name,
                'shortName'          => $request->shortName,// 门店简称
                'shopMobile'         => $request->shopMobile,
                'shopContact'        => $request->shopContact,
                'addressPrefix'      => $addressPrefix,
                "address"            => $request->address,
                "type"               => $request->type,
                "brandId"            => $request->brandId,
                'updatedAt'          => time(),
                'startAt'            => $timeChange['startAt'],
                'endAt'              => $timeChange['endAt'],
                'lng'                => $request->lng,
                'lat'                => $request->lat,
                'branch'             => $request->branch,
                'province'           => $request->province,
                'city'               => $request->city,
                'district'           => $request->district,
                'privateDrainageImg' => str_replace(config('other.IMAGE_URL'), '', $request->privateDrainageImg),
                'orderingQrCode'     => str_replace(config('other.IMAGE_URL'), '', $request->orderingQrCode),
                'updatedByUid'       => AuthService::getUserId(),
                'serialNumber'       => $request->serialNumber,
                'formalUseAt'        => empty($request->formalUseAt) ? 0 : strtotime($request->formalUseAt . ' 00:00:00'),
                'status'             => empty($request->status) ? ShopEnum::SHOP_STATUS_NORMAL : $request->status,
                'cashRegisterStatus' => empty($request->cashRegisterStatus) ? ShopEnum::SHOP_CASH_REGISTER_STATUS_OK : $request->cashRegisterStatus,
                'councilorId'        => $request->councilorId ?? 0,
                'cameraSupplierId'   => $request->cameraSupplierId ?? 0,
                'cameraSupplier'     => $request->cameraSupplier ?? 0
            ];

            $shopArchives = [
                'wechat'           => $request->archives['wechat'] ?? '',
                'wechatUrl'        => $request->archives['wechatUrl'] ?? '',
                'wechatPic'        => $request->archives['wechatPic'] ?? '',
                'eWechat'          => $request->archives['eWechat'] ?? '',
                'eWechatUrl'       => $request->archives['eWechatUrl'] ?? '',
                'eWechatPic'       => $request->archives['eWechatPic'] ?? '',
                'manageEWechatPic' => $request->archives['manageEWechatPic'] ?? '',
                'manageEWechatUrl' => $request->archives['manageEWechatUrl'] ?? '',
            ];

//            dd($shopArchives);
//            $shopArchives         = array_filter($shopArchives);
            $data['shopArchives'] = $shopArchives;
            self::updateShopInfo(['id' => $request->shopId], $data);
        } catch (\Exception $exception) {
            throw new ShopException(ShopCodeEnum::ERR_SHOP_UPDATE,
                ShopCodeEnum::CODE_TEXT[ShopCodeEnum::ERR_SHOP_UPDATE], $exception);
        }
    }

    public static function deletedShop($request)
    {
        if (!Shop::query()->where(['id' => $request->shopId])->first()) {
            throw new ShopException(ShopCodeEnum::ERR_SHOP_ID_NO_EXIST);
        }

        self::updateShopInfo(['id' => $request->shopId],
            ['isDeleted' => 1, 'updatedByUid' => AuthService::getUserId()]);
    }

    /**
     * 更新店铺状态
     *
     * @param $shopId
     * @param $status
     * @throws ShopException
     */
    public static function setShopStatus($shopId, $status)
    {
        $shop = ShopService::getShopInfo($shopId);

        // 筹备中不允许禁用
        if ($shop->status == ShopEnum::SHOP_STATUS_PREPARE && $status == ShopEnum::SHOP_STATUS_BAN) {
            throw new ShopException(ShopCodeEnum::SHOP_PREPARE_NOT_BAN);
        }

        self::updateShopInfo(['id' => $shopId], ['status' => $status]);
    }

    /**
     * 统一 更新店铺信息
     *
     * @param array $conditions 条件
     * @param array $updateField 更新字段
     * @param int $from 来源 1 RMS 2 TIGER
     * @throws BaseException
     * @throws GuzzleException
     * @throws InvalidArgumentException
     * @throws ShopException
     * @throws UserException
     * @throws Exception
     */
    public static function updateShopInfo(array $conditions, array $updateField = [], int $from = 1, bool $fsLog = true)
    {
        $shop = Shop::query()->where($conditions)->first();

        if (empty($shop)) {
            throw new ShopException(ShopCodeEnum::ERR_SHOP_ID_NO_EXIST);
        }
        $original = clone $shop;

        // 同步门店属性
        $updateShopAttribute = [];

        try {
            $shopArchives = $updateField['shopArchives'] ?? [];
            unset($updateField['shopArchives']);
            foreach ($updateField as $field => $value) {
                $shop->$field = $value;
                if (in_array($field, ['city', 'type'])) {
                    $updateShopAttribute[$field] = $value;
                }
            }
            $shop->save();
            ShopArchives::query()->updateOrCreate(['shopId' => $shop->id], $shopArchives);
        } catch (\Exception $exception) {
            throw new ShopException(ShopCodeEnum::ERR_SHOP_UPDATE,
                ShopCodeEnum::CODE_TEXT[ShopCodeEnum::ERR_SHOP_UPDATE], $exception);
        }

        if ($fsLog) {
            CommonService::fsLogOperate(
                AuditEventCodeEnum::EVENT_TYPE_10000,
                $original->toArray(),
                $updateField,
                'kf_shops',
                ['shopInfo' => Shop::query()->where($conditions)->first()->only(['id', 'name'])]
            );
        }

        // 判断督导字段是否修改了
        if (isset($updateField['councilorId']) && $original->councilorId != $updateField['councilorId'] && $from == 1) {
            $councilInfo = UserService::getUserInfoById($updateField['councilorId']);
            OpenShopRemote::setUserManageId($original->shopCode, 5, $councilInfo->id, $councilInfo->username);
        }

        Cache::forget(ShopCacheEnum::getShopInfo($shop->id));
        Cache::forget(ShopCacheEnum::getShopArchivesInfo($shop->id));
        ShopService::delSelectionCache();

        // 同步门店属性
        if ($updateShopAttribute) {
            ShopAttributeService::updateShopAttributeByShopEdit($shop->id, $updateShopAttribute);
        }
    }

    /**
     * 给门店下发菜单
     *
     * 门店的菜单和底层菜单做软连。不单独做镜像。
     * @param Request $request
     * @throws ShopException
     */
    public static function bindProductMenu(Request $request): void
    {
        $shopIds       = $request->post('shopIds');
        $productMenuId = $request->post('productMenuId');

        $productMenu = ProductMenu::query()->find($productMenuId);
        if (!$productMenu || $productMenu->status != DBEnum::STATUS_ON) {
            throw new ShopException(ShopCodeEnum::ERR_MENU_OFFLINE);
        }

        // 获取现有该菜单的门店
        $haveMenuShopIds = Shop::query()
                               ->select('id')
                               ->where('menuId', $productMenuId)
                               ->get()
                               ->pluck('id');
        $diff            = collect($shopIds)->diff($haveMenuShopIds);
        $shopIds         = $diff->values()->toArray();

        try {
            Shop::setProductMenuIds($shopIds, $productMenuId);
        } catch (\Exception $exception) {
            throw new ShopException(ShopCodeEnum::ERR_SHOP_BIND, ShopCodeEnum::CODE_TEXT[ShopCodeEnum::ERR_SHOP_BIND],
                $exception);
        }


        MqttQueueService::updateProductMenu($shopIds);
    }

    /**
     * 根据时间得出店铺营业时间分钟数
     * @param $startAt
     * @param $endAt
     * @return array
     */
    public static function shopBusinessTimeChange($startAt, $endAt): array
    {
        [$startAtHour, $startMinute] = explode(":", $startAt);
        [$endHour, $endMinute] = explode(":", $endAt);
        //如果结束时间小于开始时间，说明是次日营业
        if ($endHour < $startAtHour) {
            //计算得到次日几点
            //计算相差多少个小时
            $differHour = 24 + $endHour;
            //计算相差多少分钟
//            $differMinute = 60 - $startMinute + $endMinute;
            //开始营业时间分钟数
            $startAt = $startAtHour * 60 + $startMinute;
            //停业时间分钟数
            $endAt = $differHour * 60 + $endMinute;
        } else {
            $startAt = $startAtHour * 60 + $startMinute;
            $endAt   = $endHour * 60 + $endMinute;
        }
        return ['startAt' => $startAt, 'endAt' => $endAt];
    }

    /**
     * 根据数据库时间数值得出时间
     * @param $startAt
     * @param $endAt
     * @return string[]
     */
    public static function getShopBusinessTimeChangeByDb($startAt, $endAt): array
    {
        //开始时间取小时
        $startHour = floor($startAt / 60);
        $startHour = $startHour >= 10 ? $startHour : "0" . $startHour;
        //开始时间取分钟
        $startMinute = $startAt % 60;
        if ($startMinute == 0) {
            $startMinute = '00';
        }
        //结束时间取小时
        $endHour = floor($endAt / 60);
        $endHour = $endHour >= 10 ? $endHour : "0" . $endHour;
        //结束时间取分钟
        $endMinute = $endAt % 60;
        if ($endMinute == 0) {
            $endMinute = '00';
        }
        //分钟差
        //开始时间
        $startAt = $startHour . ":" . $startMinute;
        if ($endHour >= 24) {
            //次日
            $endHour = $endHour - 24;
            $endAt   = str_pad($endHour, 2, 0, STR_PAD_LEFT) . ":" . $endMinute;
        } else {
            $endAt = $endHour . ":" . $endMinute;
        }
        return ['startAt' => $startAt, 'endAt' => $endAt];
    }

    public static function foreachShopList($data)
    {
        $returnData = [];
        foreach ($data as $k => $v) {
            $shopBusinessTimeChangeByDb   = ShopService::getShopBusinessTimeChangeByDb($v['startAt'], $v['endAt']);
            $returnData[$k]['id']         = $v['id'];
            $returnData[$k]['shopCode']   = str_pad($v['shopCode'], ShopCodeEnum::CODE_TEXT[ShopCodeEnum::LEADING_ZERO],
                "0", STR_PAD_LEFT);
            $returnData[$k]['name']       = $v['name'];
            $returnData[$k]['shopMobile'] = $v['shopMobile'];
            $returnData[$k]['address']    = $v['address'];
            $returnData[$k]['status']     = $v['status'];
            $returnData[$k]['updatedAt']  = $v['updatedAt'];
            $returnData[$k]['createdAt']  = $v['createdAt'];
            $returnData[$k]['startAt']    = $shopBusinessTimeChangeByDb['startAt'];
            $returnData[$k]['endAt']      = $shopBusinessTimeChangeByDb['endAt'];
            $returnData[$k]['createdBy']  = $v['createdByUidRel']['username'];
            $returnData[$k]['updatedBy']  = $v['updatedByUidRel']['username'];
            $bind                         = [
                'meituanWaimai'  => [
                    'bound'      => false,
                    'bindUrl'    => '',
                    'unbindUrl'  => '',
                    'shopName'   => '',
                    'sourceType' => WaiMaiDBEnum::SOURCE_TYPE_MT_WM,
                ],
                'elemeWaimai'    => [
                    'bound'      => false,
                    'bindUrl'    => '',
                    'unbindUrl'  => '',
                    'shopName'   => '',
                    'sourceType' => WaiMaiDBEnum::SOURCE_TYPE_ELM,
                ],
                'meituanTuangou' => [
                    'bound'      => false,
                    'bindUrl'    => '',
                    'unbindUrl'  => '',
                    'shopName'   => '',
                    'sourceType' => WaiMaiDBEnum::SOURCE_TYPE_MT_TG,
                ],
            ];
            foreach ($v->waimaiShopMappings as $waimaiShopMapping) {
                switch ($waimaiShopMapping->sourceType) {
                    case WaiMaiDBEnum::SOURCE_TYPE_MT_WM:
                        $bind['meituanWaimai']['bound']    = true;
                        $bind['meituanWaimai']['shopName'] = $waimaiShopMapping->thirdShopName;
                        break;
                    case WaiMaiDBEnum::SOURCE_TYPE_ELM:
                        $bind['elemeWaimai']['bound']    = true;
                        $bind['elemeWaimai']['shopName'] = $waimaiShopMapping->thirdShopName;
                        break;
                    case WaiMaiDBEnum::SOURCE_TYPE_MT_TG:
                        $bind['meituanTuangou']['bound']    = true;
                        $bind['meituanTuangou']['shopName'] = $waimaiShopMapping->thirdShopName;
                        break;
                }
            }
            // 补充网址
            foreach ($bind as $kk => $vv) {
                if ($vv['bound']) {
                    $bind[$kk]['unbindUrl'] = (new WaiMaiShopService($v->id))->unbindUrl($vv['sourceType']);
                } else {
                    $bind[$kk]['bindUrl'] = (new WaiMaiShopService($v->id))->bindUrl($vv['sourceType']);
                }
            }
            $returnData[$k]['bind'] = $bind;
        }
        return $returnData;
    }

    /**
     * 修改店铺菜品的自定义状态
     * @param int $productId
     * @param int|null $status
     * @param int|null $out
     * @return bool
     * @throws ProductException
     * @throws ProductMenuException
     */
    public static function doProductStatus($productId, $status, $out): bool
    {
        $shopId  = request()->header('shopId');
        $brandId = request()->header('brandId');

        $shop   = ShopService::getShopInfo($shopId);
        $menuId = $shop->menuId ?? 0;
        if (!$menuId) {
            throw new ShopException(ShopCodeEnum::ERR_SHOP_NO_MENU);
        }

        $product = Product::checkProductId($brandId, $menuId, $productId);
        if (!$product) {
            throw new ProductException(ProductCodeEnum::ERR_PRODUCT_PRODUCT_NOT_IN_SHOP);
        }

        if (!ShopProductStatus::addData($brandId, $shopId, $productId, $product->price, $product->showPlatform, $status,
            $out)) {
            throw new ProductMenuException(ProductMenuCodeEnum::ERR_FOOD_MENUS_DB_SET);
        }

        Cache::forget(ShopCacheEnum::getCustomShopProductStatus($shopId));

        MqttQueueService::updateProductMenu([$shopId]);


        return true;
    }

    /**
     * 获取店铺的自定义状态
     * @param int $shopId
     * @param array $productIds
     */
    public static function sGetStatusList(int $shopId, array $productIds = [])
    {
        $list = Cache::remember(ShopCacheEnum::getShopProductStatus($shopId), 5, function () use ($shopId) {
            return ShopProductStatus::getStatusList($shopId);
        });

        if (!$productIds) {
            return $list;
        }

        return $list->filter(function ($item) use ($productIds) {
            if (in_array($item['productId'], $productIds)) {
                return true;
            }

            return false;
        })->values();
    }

    /**
     * 查询定制菜品列表
     * @param int $shopId
     * @param array $productIds
     */
    public static function sGetCustomStatusList(int $shopId, array $productIds = [])
    {
        $list = Cache::remember(ShopCacheEnum::getCustomShopProductStatus($shopId), getSwitchCacheTime(86400),
            function () use ($shopId) {
                return ShopProductStatus::getCustomProductList($shopId);
            });

        if (!$productIds) {
            return $list;
        }

        return $list->filter(function ($item) use ($productIds) {
            if (in_array($item['productId'], $productIds)) {
                return true;
            }

            return false;
        })->values();
    }

    // 获取当前用户有的店铺列表
    public static function getShopListByUserAndPlatform(
        int $userId,
        int $platform = CodeEnum::PLATFORM_B_BRAND,
        array $condition = ['status' => ShopEnum::SHOP_STATUS_NORMAL]
    ) {
        $shopIdListIds = array_unique(UserRoleMapping::getShopListByUserId($userId, $platform));

        if (empty($shopIdListIds)) {
            return [];
        }

        $shopIdListInfo = self::getShopListByIds($shopIdListIds, $condition);

        return $shopIdListInfo;
    }

    /**
     *
     * [区分平台] 通过用户 Id, 平台 Id 获取店铺列表
     *
     * @param int $userId
     * @param int $platform
     * @return array
     */
    public static function getShopListByUserId(int $userId, int $platform = CodeEnum::PLATFORM_B_BRAND): array
    {
        $shopIdListIds = UserRoleMapping::getUserShopListByUserId($userId, $platform);

        if (empty($shopIdListIds)) {
            return [];
        }

        $shopIdListInfo = self::getShopListByIds($shopIdListIds,
            ['status' => ShopEnum::SHOP_STATUS_NORMAL]);

        return $shopIdListInfo;
    }

    public static function getShopListByShopIds($shopIds, array $field = ['name', 'id', 'shortName'])
    {
        return Shop::query()
                   ->whereIn('id', $shopIds)
                   ->select($field)
                   ->get();
    }

    /**
     * 添加商家运营目标
     * @param $shopId
     * @param $target
     * @param $createdByUid
     * @return bool
     * @throws ShopException
     */
    public static function addShopTarget($shopId, $target, $brandId): bool
    {
        //获得本月开始时间到结束时间的时间戳
        $monthTime     = BaseService::getNowMonthStartToEnd();
        $operateTarget = self::getTargetIsSet($shopId, $brandId, $monthTime);
        if ($operateTarget != null) {
            //已设置
            ShopOperateTargetModel::query()
                                  ->where('id', '=', $operateTarget->id)
                                  ->update(['target' => $target]);
        } else {
            //未设置
            ShopOperateTargetModel::addData(
                $shopId,
                $target,
                $brandId
            );
        }
        return true;
    }

    //判断是否已经设置门店运营目标
    public static function getTargetIsSet($shopId, $brandId, $time)
    {
        return ShopOperateTargetModel::query()
                                     ->where('shopId', '=', $shopId)
                                     ->where('brandId', '=', $brandId)
                                     ->whereBetween('createdAt', $time)
                                     ->first();
    }

    /**
     * 设置店铺配置信息
     * @param $shopId
     * @param $isBusiness
     * @param $appletStatus
     * @param array $timeChange
     * @param string $mobile
     * @return Builder[]
     * @throws GuzzleException
     * @throws InvalidArgumentException
     * @throws ShopException
     */
    public static function setShopConfigInfo(
        $shopId,
        $isBusiness,
        $appletStatus,
        array $timeChange = [],
        string $mobile = "",
        array $shopArchives = [],
        array $updateData = []
    ) {
        try {
            $update = [
                'appletStatus' => $appletStatus,
                'isBusiness'   => $isBusiness
            ];
            if (!empty($timeChange)) {
                $update['startAt'] = $timeChange['startAt'];
                $update['endAt']   = $timeChange['endAt'];
            }

            if (!empty($mobile)) {
                $update["shopMobile"] = $mobile;
            }
            $update['shopArchives'] = $shopArchives;
            $update                 = array_merge($update, $updateData);
            self::updateShopInfo(['id' => $shopId], $update);
            return $shopId;
        } catch (\Exception $exception) {
            throw new ShopException(ShopCodeEnum::ERR_SHOP_SET_CONFIG, '', $exception);
        }
    }

    /**
     * 商家APP门店下的所有员工，需要过滤掉有运营中心权限的
     * @param $shopId
     * @param $status
     * @param bool $filterWebUser
     * @param array $type
     * @return array[]|Builder[]
     */
    public static function getShopStaffInfo(
        $shopId,
        $status,
        bool $filterWebUser = false,
        array $type = [RoleCodeEnum::ROLE_BUSINESS_TYPE, RoleCodeEnum::ROLE_POS_TYPE]
    ): array {
        $userList = UserRoleMapping::getUserIdsByShopIds($shopId, $type);

        // 需要过滤掉的用户ids
        if ($filterWebUser) {
            $filterUserIds = UserRoleMapping::getWebUserIds();
        } else {
            $filterUserIds = [];
        }

        $count     = 0;
        $departure = 0;
        $normal    = 0;
        $staff     = [];

        // 循环获取用户信息
        foreach ($userList as $list) {
            if (empty($list['user_rel']) || empty($list['roleInfo'])) {
                continue;
            }

            $userInfo = $list['user_rel'];
            $roleInfo = $list['roleInfo'];

            $userId = $userInfo['id'];

            if (key_exists($userId, $staff)) {
                $array = [
                    "roleId"   => $roleInfo['id'],
                    "roleName" => $roleInfo['name']
                ];
                array_push($staff[$userId]['role'], $array);
            } else {
                if ($status != $userInfo['status']) {
                    continue;
                }

                $staff[$userId] = [
                    'id'                => (string)$userId,
                    'role'              => [
                        [
                            "roleId"   => $roleInfo['id'],
                            "roleName" => $roleInfo['name']
                        ]
                    ],
                    "username"          => $userInfo['username'],
                    "mobile"            => $userInfo['mobile'],
                    "status"            => UserCodeEnum::USER_STATUS_TEXT[$userInfo['status']],
                    'statusInt'         => $userInfo['status'],
                    'avatar'            => $userInfo['avatar'],
                    'entryTime'         => $list['entryTime'],
                    'inShop'            => $list['inShop'],
                    'cardStatus'        => $list['cardStatus'],
                    'appletCardStatus'  => $list['appletCardStatus'],
                    'idCardStatus'      => $list['idCardStatus'],
                    'healthCardStatus'  => $list['healthCardStatus'],
                    'healthCardEndDate' => $list['user_rel']['healthCardEndDate'] ? Carbon::createFromTimestamp($list['user_rel']['healthCardEndDate'])->toDateString() : '',
                    'healthCardPhoto'   => strlen($list['user_rel']['healthCardPhoto']) ? (FileUtil::domain() . '/' . $list['user_rel']['healthCardPhoto']) : '',
                ];
            }

            // 离职不显示职位
            if (isset($staff[$userId]['statusInt']) && $staff[$userId]['statusInt'] == UserCodeEnum::USER_STATUS_STOP) {
                $staff[$userId]['role'] = [];
            }
        }

        foreach ($staff as $userId => $v) {
            if (in_array($userId, $filterUserIds)) {
                unset($staff[$userId]);
                continue;
            }

            if ($v['statusInt'] == UserCodeEnum::USER_STATUS_STOP) {
                $departure++;
            }
        }

        $normal = count($staff) - $departure;
        return [
            'count'     => $count,
            'departure' => $departure,
            'normal'    => $normal,
            'list'      => array_values($staff)
        ];
    }

    public static function getRoleByUserId($userId, $shopId, $type = 0)
    {
        $roles = UserRoleMapping::query()
                                ->where('userId', '=', $userId)
                                ->where('shopId', '=', $shopId)
                                ->when($type != 0, function ($query) use ($type) {
                                    $query->where('type', $type);
                                })
                                ->with('rolesRel')
                                ->get()
                                ->toArray();
        if (!count($roles)) {
            return [];
        }
        $userRoles = [];
        foreach ($roles as $item) {
            if (!empty($item['roles_rel'])) {
                $userRole['roleId']   = $item['roles_rel']['id'];
                $userRole['roleName'] = $item['roles_rel']['name'];
                $userRole['sort'] = $item['roles_rel']['sort'] ?? 0;
                $userRoles[]          = $userRole;
            }
        }
        return collect($userRoles)->sortByDesc('sort')->values()->toArray();
    }

    /**
     * @throws OssException
     * @throws UserException
     */
    public static function getUserDetail(int $userId, int $shopId): array
    {
        //获取用户信息
        $userInfo = User::query()
                        ->where('id', $userId)
                        ->with(['createdUserRel', 'userIdCardRel'])
                        ->first();

        $shopStaffModel = ShopStaffRecordModel::query()
            ->where('shopId', $shopId)
            ->where('userId', $userId)
            ->first();

        if ($userInfo == null || $shopStaffModel == null) {
            throw new UserException(\App\Kuafu\User\Enum\UserCodeEnum::ERR_USER_NOT_EXIST);
        }

        // 获取在证件总状态
        $cardStatus       = '';
        if ($shopStaffModel->positionStatus == ShopStaffRecordModel::POSITION_STATUS_ON && $shopStaffModel->inShop == ShopStaffRecordModel::IN_SHOP_ON) {
            if ($shopStaffModel->idCardStatus == ShopStaffRecordModel::ID_CARD_STATUS_LACK || $shopStaffModel->healthCardStatus == ShopStaffRecordModel::HEALTH_CARD_STATUS_LACK) {
                $cardStatus       = '证件缺失';
            } elseif ($shopStaffModel->healthCardStatus == ShopStaffRecordModel::HEALTH_CARD_STATUS_EXPIRED) {
                $cardStatus       = '证件过期';
            } elseif ($shopStaffModel->auditStatus == ShopStaffRecordModel::AUDIT_STATUS_REJECT) {
                $cardStatus       = '未通过审查';
            } elseif ($shopStaffModel->healthCardStatus == ShopStaffRecordModel::HEALTH_CARD_STATUS_ABOUT_TO_EXPIRE) {
                $cardStatus       = '即将到期';
            }
        }

        $userRoles = self::getRoleByUserId($userId, $shopId, RoleCodeEnum::ROLE_BUSINESS_TYPE);

        //获取收银机对应角色列表数据
        $posRoles = self::getRoleByUserId($userId, $shopId, RoleCodeEnum::ROLE_POS_TYPE);

        $position = [];

        foreach ($userRoles as $role) {
            $position['chooseMerchant']['type'] = 'chooseMerchant';
            $position['chooseMerchant']['value'] = [
                'value' => $role['roleId'],
                'text'  => $role['roleName'],
            ];
            break;
        }

        foreach ($posRoles as $role) {
            $position['chooseCashier']['type'] = 'chooseCashier';
            $position['chooseCashier']['value'] = [
                'value' => $role['roleId'],
                'text'  => $role['roleName'],
            ];
            break;
        }

        $induction        = $shopStaffModel->entryTime ? date('Y-m-d', $shopStaffModel->entryTime) : 0;
        $inShop           = $shopStaffModel->inShop;
        $leaveTime        = $shopStaffModel->leaveTime ? date('Y-m-d', $shopStaffModel->leaveTime) : '';
        $isDeleted        = $shopStaffModel->positionStatus;
        $createdUserName  = User::query()->where('id', $shopStaffModel->createdByUid)->value('username');
        $updatedAt        = $shopStaffModel->updatedAt->toDateString();
        $createdAt        = $shopStaffModel->createdAt->toDateString();
        $causeOfRejection = $shopStaffModel->causeOfRejection;
        // if (
        //     isset($staffRecord[$userId]['auditStatus'])
        //     && $staffRecord[$userId]['auditStatus'] == ShopStaffRecordModel::AUDIT_STATUS_REJECT
        //     && $staffRecord[$userId]['causeOfRejection']
        // ) {
        //     $causeOfRejection = $staffRecord[$userId]['causeOfRejection'];
        // }

        return [
            'id'                 => $userInfo->id,
            'status'             => UserCodeEnum::USER_STATUS_TEXT[$isDeleted],
            'username'           => $userInfo->username,
            'induction'          => $induction,
            'isInsider'          => (bool)UserRoleMapping::query()->where('userId', $userId)->where('type', RoleCodeEnum::ROLE_WEB_TYPE)->count(),
            'leaveTime'          => $leaveTime,
            'mobile'             => $userInfo->mobile,
            'avatar'             => strlen($userInfo->avatar) ? (FileUtil::domain() . '/' . $userInfo->avatar) : '',
            'createdAt'          => $createdAt,
            'updatedAt'          => $updatedAt,
            'createdUserName'    => $createdUserName,
            'position'           => array_values($position),
            'cardStatus'         => $cardStatus,
            'role_rel'           => $userRoles,
            'posRoleLists'       => $posRoles,
            'healthCardStatus'   => $shopStaffModel->healthCardStatus != ShopStaffRecordModel::HEALTH_CARD_STATUS_UPLOADED ? $shopStaffModel->healthCardStatusText : '',
            'idCardStatus'       => $shopStaffModel->idCardStatus != ShopStaffRecordModel::ID_CARD_STATUS_UPLOADED ? $shopStaffModel->idCardStatusText : '',
            'inShop'             => $inShop, // 是否需要到店工作
            'idCardFacePhoto'    => strlen((string)optional($userInfo->userIdCardRel)->facePhoto) ? (PrivateFileUtil::signUrl(ltrim(parse_url($userInfo->userIdCardRel->facePhoto, PHP_URL_PATH), '/'))) : '',
            'idCardBackPhoto'    => strlen((string)optional($userInfo->userIdCardRel)->backPhoto) ? (PrivateFileUtil::signUrl(ltrim(parse_url($userInfo->userIdCardRel->backPhoto, PHP_URL_PATH), '/'))) : '',
            'healthPhoto'        => strlen($userInfo->healthCardPhoto) ? (FileUtil::domain() . '/' . $userInfo->healthCardPhoto) : '',
            'healthPhotoDate'    => (strlen($userInfo->healthCardPhoto) && $userInfo->healthCardEndDate != 0) ? Carbon::createFromTimestamp($userInfo->healthCardEndDate)->subYear()->toDateString() : '',
            'healthPhotoEndDate' => (strlen($userInfo->healthCardPhoto) && $userInfo->healthCardEndDate != 0) ? Carbon::createFromTimestamp($userInfo->healthCardEndDate)->toDateString() : '',
            'causeOfRejection'   => $causeOfRejection
        ];

    }

    /**
     * 获取职位列表
     *
     * @param $shopId
     * @param $brandId
     * @param int $type
     * @return array|int[]
     */
    public static function getShopPositions($shopId, $brandId, $type = RoleCodeEnum::ROLE_BUSINESS_TYPE)
    {
        $positions = Role::query()
                         ->where('shopId', $shopId)
                         ->Orwhere('shopId', 0)
                         ->where('brandId', $brandId)
                         ->where('type', $type)
                         ->orderByDesc('sort')
                         ->get();
        if ($positions == null) {
            return ['lists' => 0];
        } else {
            return [
                'lists' => $positions->map(function (Role $positions) {
                    return [
                        'id'       => $positions->id,
                        'roleName' => $positions->name,
                    ];
                })
            ];
        }

    }

    /**
     * 获取门店营运详情
     * @param int $shopId
     * @param $brandId
     * @return array[]|Builder[]
     * @throws OrderException
     */
    public static function getShopOperateDetail($shopId, $brandId): array
    {
        //获取门店信息
        $shopInfo = Shop::query()
                        ->where('id', $shopId)
                        ->first();
        //本月目标
        $monthTarget = ShopOperateTargetModel::query()
                                             ->where('shopId', '=', $shopId)
                                             ->where('brandId', '=', $brandId)
                                             ->whereBetween('createdAt', BaseService::getNowMonthStartToEnd())
                                             ->first()
            ->target;
        //日目标
        $dayTarget = $monthTarget / date('t');
        //月时间比
        $timeProgress = sprintf("%.2f", (date('d') / date('t') * 100));
        //截止今日该完成
        $completionNowDay = floor($dayTarget * date('d'));
        //截止今日该完成占总目标占比
        $percentage = sprintf("%.2f", ($completionNowDay / $monthTarget) * 100);
        //截止今日累计已完成营业收入
        $income = (int)OrderPayService::getMonthToDayActualIncome($shopId);
        //已完成的营业收入与月目标对比
        $incomePercentage = sprintf("%.2f", ($income / $monthTarget) * 100);
        return [
            'shopCode'                    => $shopInfo->shopCode,  //门店编码
            'shopName'                    => $shopInfo->name,  //门店名称
            'monthAchievementRate'        => $incomePercentage,  //月目标达成率
            'monthTimeProgressPercentage' => $timeProgress,  //月时间比
            'monthDays'                   => date('t'),  //月总天数
            'monthCumulativeIncome'       => $income,  //月累计收入
            'difference'                  => $monthTarget - $income,  //月差额
            'monthTarget'                 => $monthTarget,  //月目标
            'completionNowDay'            => $completionNowDay,  //截止今日该完成
            'dayPercentage'               => $percentage  //截止今日该完成占总目标比例
        ];
    }

    /**
     * pos 设备绑定
     *
     * @param $shopId
     * @param $serialNumber
     * @return void
     * @throws BaseException
     * @throws Exception
     * @throws GuzzleException
     * @throws InvalidArgumentException
     * @throws ShopException
     * @throws UserException
     */
    public static function bandPosSerialNumber($shopId, $serialNumber)
    {
        try {
            ShopService::updateShopInfo(['id' => $shopId], ['serialNumber' => $serialNumber]);
        } catch (ShopException $exception) {
            throw new ShopException(ShopCodeEnum::ERR_BANS_SERIAL_NUMBER);
        }

    }

    /**
     * 根据经纬度获取最近门店
     *
     * @param $lng
     * @param $lat
     * @return array
     * @throws ShopException
     */
    public static function getRecentlyShop($lng, $lat): array
    {
        return [
            'recentlyShop' => current(self::getNearShop($lng, $lat)['nearShop'])
        ];
    }

    /**
     * 获取附近门店列表
     *
     * @param $lng
     * @param $lat
     * @param int $limit
     * @param string $searchKey
     * @return array[]
     * @throws ShopException
     */
    public static function getNearShop($lng, $lat, int $limit = 10, string $searchKey = ''): array
    {
        $address = TencentMapHelp::getInstance()->inverseAddressResolution($lat, $lng);
        if ($address == false || empty($address['result']['address_component']['city'])) {
            throw new ShopException(ShopCodeEnum::ERR_GET_ADDRESS);
        }
        $city = $address['result']['address_component']['city'];

        // 获取城市code
        $cityCode  = Cache::remember('getNearShopByCode:' . $city, getSwitchCacheTime(86400),
            function () use ($city) {
                return AreaModel::query()
                                ->where('cnFullName', $city)
                                ->where('level', AreaCodeEnum::AREA_CITY)
                                ->value('areaId') ?? 0;
            });
        $fields    = [
            'id',
            'name',
            'shortName',
            'shopMobile',
            'brandId',
            'imageUrl',
            'address',
            'lng',
            'lat',
            'startAt',
            'endAt',
            'status',
            'formalUseAt',
            'isBusiness',
            'appletStatus',
            'dyCouponVerifySwitch'
        ];
        $shopLists = Shop::query()
                         ->where('shopCode', '<=', 9000)
                         ->when($cityCode || $city, function ($query) use ($city, $cityCode) {
                             if ($cityCode) {
                                 $query->where('city', $cityCode);
                             } elseif ($city) {
                                 $query->where('addressPrefix', 'like', "%{$city}%");
                             }
                         })
                         ->when($searchKey, function ($query) use ($searchKey) {
                             $query->where(function ($query) use ($searchKey) {

                                 // 支持门店名称+地址搜索(匹配门店名称和地址)
                                 $searchArr   = explode(' ', $searchKey);
                                 $searchLimit = 4;//  最大循环次数 防止循环次数过多给数据库造成压力
                                 foreach ($searchArr as $k => $search) {
                                     if ($k > $searchLimit) {
                                         break;
                                     }
                                     $query->orWhere('shortName', 'like', "%{$search}%")
                                           ->orWhere('address', 'like', "%{$search}%");
                                 }
                             });
                         })
                         ->where('status', ShopEnum::SHOP_STATUS_NORMAL)
                         ->select($fields)
                         ->with('shopArchivesRel')
                         ->get();

        $nearShop = [];
        if ($shopLists) {
            $nearShop = $shopLists
                ->where("lng", ">", 0)
                ->where("lat", ">", 0)
                ->each(function ($v) use ($lat, $lng) {
                    $v->distance = CommonService::getDistance($lat, $lng, $v->lat, $v->lng) * 1000;
                    ShopService::resolveShopStatus($v);
                })
                ->sortBy("openStatus")
                ->sortBy('distance')
                ->each(function ($item) {
                    $item['isOpenSelfDelivery'] = $item->shopArchivesRel->isOpenSelfDelivery ?? 0;
                    unset($item->shopArchivesRel);
                })
                ->map(function ($item) {
                    if (mb_substr($item->name, -2, 2) != '的店' && strlen($item->shortName)) { // 使用个人名称的店 不显示
                        return $item;
                    }
                })
                ->filter()
                ->values()
                ->toArray();
        }
        array_splice($nearShop, $limit);
        return [
            'nearShop' => $nearShop
        ];
    }

    /**
     * 获取常去门店列表
     *
     * @param Request $request
     * @return array[]
     */
    public static function getUsuallyShop(Request $request): array
    {
        $userId            = CustomerPlatformService::getUserId($request);
        $cacheKey          = ShopCacheEnum::getShopUserUsually($userId);
        $topHundred        = RedisHelp::getInstance()->getRedisInstance()->zRange($cacheKey, 0, 99, true);
        $responseShopLists = [];
        if (!empty($topHundred)) {
            $shopIds   = array_keys($topHundred);
            $fields    = [
                'id',
                'name',
                'shopMobile',
                'brandId',
                'imageUrl',
                'address',
                'lng',
                'lat',
                'startAt',
                'endAt',
                'status',
                'isBusiness',
                'appletStatus'
            ];
            $shopLists = Shop::query()
                             ->whereIn('id', $shopIds)
                             ->where('status', ShopEnum::SHOP_STATUS_NORMAL)
                             ->select($fields)
                             ->get();
            if (!empty($shopLists)) {
                $responseShopLists = $shopLists->each(function ($v) use ($topHundred) {
                    $v->score = $topHundred[$v->id];
                    ShopService::resolveShopStatus($v);
                })->sortByDesc('score')->values()->toArray();
            }
        }

        return ['shopLists' => $responseShopLists];
    }

    /**
     * 解析门店营业状态
     *
     * @param Shop $shop
     */
    public static function resolveShopStatus(Shop $shop)
    {
        $time = ShopService::getShopBusinessTimeChangeByDb($shop->startAt, $shop->endAt);

        $openStatus = self::getShopOpenStatus($shop);

        // 营业状态
        $shop->openStatus = $openStatus['openStatus'];
        $shop->startAt    = $time['startAt'];
        $shop->endAt      = $time['endAt'];
    }

    /**
     * @param $userId
     * @param $shopId
     * @return int
     */
    public static function shopUserUsuallyAdd($userId, $shopId): int
    {
        if ($userId == 0) {
            return 0;
        }
        return RedisHelp::getInstance()->getRedisInstance()->zadd(
            ShopCacheEnum::getShopUserUsually($userId),
            time(),
            $shopId
        );
    }

    public static function getShopOpenStatus(Shop $shop)
    {
        $startAt = $shop->startAt;
        $endAt   = $shop->endAt;

        $time = ShopService::getShopBusinessTimeChangeByDb($startAt, $endAt);

        $startAt = str_replace(":", "", $time["startAt"]);
        $endAt   = str_replace(":", "", $time["endAt"]);
        $timeNow = date("Hi");
        if ($shop->isBusiness == ShopEnum::SHOP_BUSINESS_STATUS_SUSPEND) {
            $openStatus = ShopEnum::OPEN_STATUS_TIME_END;
        } else {
            $timeGroup = [];
            if ($endAt <= $startAt) {
                array_push($timeGroup, [
                    "startAt" => $startAt,
                    "endAt"   => 2400
                ], [
                    "startAt" => 0,
                    "endAt"   => $endAt
                ]);
            } else {
                array_push($timeGroup, [
                    "startAt" => $startAt,
                    "endAt"   => $endAt
                ]);
            }
            $openStatus = ShopEnum::OPEN_STATUS_END;
            foreach ($timeGroup as $timeRange) {
                if ($timeNow > $timeRange["startAt"] && $timeNow < $timeRange["endAt"]) {
                    $openStatus = ShopEnum::OPEN_STATUS_START;
                    break;
                }
            }


//        $now = date('H:i');
//        list($h, $m) = explode(':', $now);
//        $now = $h * 60 + $m;
//
//        if ($shop->isBusiness == ShopEnum::SHOP_BUSINESS_STATUS_SUSPEND) {
//            $openStatus = ShopEnum::OPEN_STATUS_TIME_END;
//        } else {
//            if ($endAt < 1440) {
//                if ($now > $startAt && $now < $endAt) {
//                    $openStatus = ShopEnum::OPEN_STATUS_START;
//                } else {
//                    $openStatus = ShopEnum::OPEN_STATUS_END;
//                }
//            } else {
//                // 判断当前时间 是否超了 0 点
//                if ($now > $startAt) {
//                    $openStatus = ShopEnum::OPEN_STATUS_START;
//                } else {
//                    if ($now < $endAt) {
//                        $openStatus = ShopEnum::OPEN_STATUS_START;
//                    } else {
//                        $openStatus = ShopEnum::OPEN_STATUS_END;
//                    }
//                }
//            }
        }

        return [
            'openStatus'   => $openStatus,
            'appletStatus' => $shop->appletStatus,
            'openTime'     => [
                'startAt' => $time['startAt'],
                'endAt'   => $time['endAt']
            ]
        ];
    }

    /**
     * 正式使用门店列表
     *
     * @param int $formalUsefulTime
     * @return Builder[]
     */
    public static function getUsefulShopLists(int $formalUsefulTime): array
    {
        return [
            'shopLists' => Shop::getUsefulShopLists($formalUsefulTime)->map(function ($v) {
                $v->shopCode = CommonService::zeroFill($v->shopCode);
                return $v;
            })->values()->toArray()
        ];
    }

    /**
     * 内网创建店铺
     * @param Request $request
     * @return int
     * @throws ShopException
     * @throws UserException
     */
    public static function createShopInternal(Request $request): array
    {
        $brandId                     = $request->get('brandId');
        $shopName                    = $request->get('shopName');
        $shopCode                    = $request->get('shopCode');
        $shopMobile                  = $request->get('shopMobile');
        $contact                     = $request->get('contact'); // 联系人
        $address                     = $request->get('address'); // 详细地址
        $type                        = $request->get('type'); // 1加盟 2直营
        $request->name               = $shopName;
        $request->startAt            = '00:00';
        $request->endAt              = '00:00';
        $request->branch             = '';
        $request->privateDrainageImg = '';
        $request->orderingQrCode     = '';
        $request->formalUseAt        = '';
        $request->lng                = '';
        $request->lat                = '';
        $adRegionCode                = $request->get('adRegionCode'); // 区域高德编码
        $adCityCode                  = $request->get('adCityCode'); // 城市高德编码
        $regionName                  = $request->get('regionName'); // 区域名
        $status                      = (int)$request->get('status'); // 店铺状态

        if ($status == ShopEnum::SHOP_STATUS_PREPARE) {
            $request->province = $request->city = $request->district = 0;

            $shopCode = $request->shopCode = self::chooseShopCode((int)$shopCode);

        } else {
            $areaId = AreaModel::getAreaIdByAdCode($adRegionCode);
            if (!$areaId) {

                // 备用方案，用城市编码+区域名称来查areaId
                $areaId = AreaModel::getAreaByCityCodeRegionName((int)$adCityCode, $regionName);
            }

            list($request->province, $request->city, $request->district) = AreaModel::getFullCode($areaId);
        }

        // 1. 门店已存在
        $shop = Shop::query()->where('shopCode', $shopCode)->first();
        if ($shop) {
            throw new ShopException(ShopCodeEnum::ERR_SHOP_CODE_EXISTS);
        }
        $shopId = self::addShop($request, false);

        // 配置的商家app店长角色id
        $managerRoleId = config('merchant.manager_role_id');
        $role          = Role::query()->find($managerRoleId);
        if (!$role || strpos($role->name, '店长') === false) {
            $roleName = $role->name ?? '';
            SendFs::newRobot()->setTitleContent('自动添加商家APP店长异常',
                "role_id默认值异常，role_id:{$managerRoleId} name:{$roleName} shopId:{$shopId}");
        }

        // 配置的收银pos店长角色id
        $posManagerRoleId = config('pos.manager_role_id');
        $posRole          = Role::query()->find($posManagerRoleId);
        if (!$posRole || strpos($posRole->name, '店长') === false) {
            $posRoleName = $posRole->name ?? '';
            SendFs::newRobot()->setTitleContent('自动添加pos店长异常',
                "role_id默认值异常，role_id:{$posManagerRoleId} name:{$posRoleName} shopId:{$shopId}");
        }

        // 判断手机号用户是否存在
        $user = User::withOutGlobalScope('isDel')->where('brandId', $brandId)->where('mobile', $shopMobile)
                    ->first();
        if ($user && $user->isDel == 1) {
            $user->contact = $contact;
            $user->isDel   = 0;
            $user->save();
        }

        if ($user && $user->status != UserCodeEnum::USER_STATUS_USE) {
            $user->status = UserCodeEnum::USER_STATUS_USE;
            $user->save();
        }

        if (!$user) {

            // 添加用户
            $password = UserService::generateSimplePwd(8);
            $user     = UserService::addUserDB($brandId, $contact, $shopMobile, $password);
        }

        // 判断用户是否有商家店长权限
        if (!UserRoleService::checkRoleInShop($shopId, $managerRoleId, RoleCodeEnum::ROLE_BUSINESS_TYPE, $user->id)
                            ->count()) {

            // 添加商家店长权限
            UserRoleMapping::addUserRoleMap($user->id, $shopId, $managerRoleId, $brandId,
                RoleCodeEnum::ROLE_BUSINESS_TYPE);
        }

        // 判断用户是否有pos店长权限
        if (!UserRoleService::checkRoleInShop($shopId, $posManagerRoleId, RoleCodeEnum::ROLE_POS_TYPE, $user->id)
                            ->count()) {

            // 添加商家店长权限
            UserRoleMapping::addUserRoleMap($user->id, $shopId, $posManagerRoleId, $brandId,
                RoleCodeEnum::ROLE_POS_TYPE);
        }

        return [$shopId, $shopCode, $password ?? ''];
    }

    /**
     * 按逻辑处理生成shopCode
     * @param int $shopCode
     * @return int
     * @throws ShopException
     */
    public static function chooseShopCode(int $shopCode): int
    {
        if ($shopCode) {

            // 手动传入shopCode - 目前仅支持比生成器小的shopCode
            if (config('app.env') != 'qa' && $shopCode > (new ShopCodeService())->getShopCode()) {
                throw new ShopException(ShopCodeEnum::ERR_SHOP_CODE_IN_MAX);
            }

            if (Shop::query()->withoutGlobalScope('isDel')->where('shopCode', $shopCode)->selectRaw(1)->count()) {
                throw new ShopException(ShopCodeEnum::ERR_SHOP_CODE_REPEAT);
            }

            return $shopCode;

        } else {

            // 自动生成shopCode
            return (new ShopCodeService())->createShopCode();
        }
    }

    /**
     * @param $params
     * @param $uid
     * @param $shopId
     * @param int $payChannel
     * @throws ShopException
     */
    public static function shopPaymentsInsert($params, $uid, $shopId, int $payChannel = PayStreamModel::PAY_CHANNEL_FY)
    {
        try {
            $shop = ShopService::getShopInfo($shopId);
            if (!$shop) {
                throw new ShopException(ShopCodeEnum::ERR_SHOP_ID_NO_EXIST);
            }

            if (ShopPaymentsModel::query()->where('shopId', $shopId)->where('payChannel', $payChannel)->exists()) {
                throw new \Exception(
                    sprintf(
                        '当前门店支付渠道[ %s ]已存在，请勿重复添加',
                        PayStreamModel::PAY_CHANNEL_TEXT[$payChannel]
                    )
                );
            }

            // 存在终端号时验证是否已经绑定了其他门店
            self::checkReservedFyTermId($params, $shopId, $payChannel);
            $insert = array_merge(collect((array)$params)->except(["source"])->toArray(), [
                'isOpen'       => 1,
                'paymentName'  => '富有直连',
                'shopId'       => $shopId,
                'payChannel'   => $payChannel,
                'updatedAt'    => time(),
                'updatedByUid' => $uid,
                'createdAt'    => time(),
                'createdByUid' => $uid
            ]);

            ShopPaymentsModel::query()->insert($insert);

            CommonService::fsLogOperate(
                AuditEventCodeEnum::EVENT_TYPE_10002,
                [],
                $insert,
                'kf_shop_payments',
                ['shopInfo' => Shop::query()->where('id', $shopId)->first()->only(['id', 'name'])]
            );

            self::shopPaymentsChangeFromWorkOrder(attributeGet($params, "source", 0), $shopId);

            //通过shopId拿到店长ID
            $userIds = UserRoleService::getShopManageList(config('merchant.manager_role_id'), [$shopId])['lists'] ?? [];
            InformationPushService::push(InformationPushService::KF_OPEN_PUSH, array_column($userIds, 'userId'), $shop);

        } catch (\Exception $e) {
            throw new ShopException(ShopCodeEnum::ERR_SHOP_BIND, $e->getMessage());
        }
    }


    /**
     * @param $params
     * @param $uid
     * @param $shopId
     * @param int $payChannel
     * @throws ShopException
     */
    public static function shopPaymentsEdit($params, $uid, $shopId, int $payChannel = PayStreamModel::PAY_CHANNEL_FY)
    {
        try {

            // 存在终端号时验证是否已经绑定了其他门店
            self::checkReservedFyTermId($params, $shopId, $payChannel);
            $original = ShopPaymentsModel::query()->where('id', $params['id'])->first()->toArray();
            $edit     = array_merge(collect((array)$params)->except(["source"])->toArray(), [
                'payChannel'   => $payChannel,
                'updatedAt'    => time(),
                'updatedByUid' => $uid
            ]);
            ShopPaymentsModel::query()->where('id', $params['id'])->update($edit);

            CommonService::fsLogOperate(
                AuditEventCodeEnum::EVENT_TYPE_10003,
                $original,
                $edit,
                'kf_shop_payments',
                ['shopInfo' => Shop::query()->where('id', $shopId)->first()->only(['id', 'name'])]
            );

            self::shopPaymentsChangeFromWorkOrder(attributeGet($params, "source", 0), $shopId);
        } catch (\Exception $e) {
            throw new ShopException(ShopCodeEnum::ERR_SHOP_BIND, $e->getMessage());
        }
    }

    /**
     * 收银进件2.0工单完成 启用店铺&下发全国统一菜单【统一自选（标准）】
     *
     * @param int $source
     * @param int $shopId
     * @throws BaseException
     * @throws GuzzleException
     * @throws InvalidArgumentException
     * @throws ShopException
     * @throws UserException
     * @throws Exception
     */
    public static function shopPaymentsChangeFromWorkOrder(int $source, int $shopId)
    {
        if ($source) {
            //功能开通 - 收银进件2.0完成
            //-----------------------------
            $shopModel  = Shop::query()->where('id', $shopId)->first();
            $lastMenuId = $shopModel->menuId;
            $updateData = [];

            if ($shopModel->status != ShopEnum::SHOP_STATUS_NORMAL) {
                // todo 启用店铺
                $updateData["status"] = ShopEnum::SHOP_STATUS_NORMAL;
            }

            if (!$lastMenuId) {
                // todo 下发全国统一菜单【统一自选（标准）】
                $menuId = self::shopMatchMenu($shopModel);
                if ($menuId) {
                    $updateData["menuId"] = $menuId;
                }
            }

            if ($updateData) {
                ShopService::updateShopInfo(['id' => $shopId], $updateData);
            }

            if (isset($updateData["menuId"])) {
                //更新菜单
                MqttQueueService::updateProductMenu([$shopId]);
            }
        }
    }

    /**
     * 根据城市匹配门店菜单
     *
     * @param Shop $shop
     * @return int|mixed
     * @throws BaseException
     */
    public static function shopMatchMenu(Shop $shop)
    {
        $city   = explode("-", $shop->addressPrefix)[1] ?? "";
        $menuId = 0;
        if (!empty($city)) {
            $cityClassifiedStoreMenu = ConfigService::getConfigDetail("menu", "cityClassifiedStoreMenu")["value"];
            collect($cityClassifiedStoreMenu)->each(function ($menuCityGroup) use ($city, &$menuId) {
                if (in_array($city, $menuCityGroup["cityNameGroup"])) {
                    $menuId = $menuCityGroup["menuId"];
                    return false;
                }
            });
        }
        return $menuId;
    }

    /**
     * 门店选择器
     * @param int $isCity
     * @param string $status
     * @param int $openFyStatus
     * @param int $isDistrict
     * @return array
     */
    public static function shopSelect(
        int $isCity = 0,
        string $status = "",
        int $openFyStatus = 0,
        int $isDistrict = 0
    ): array
    {
        if ($isCity) {// 只返回分公司-城市
            return [
                'shopSelect' => self::getSelectionOnlyCityCache()
            ];
        }
        if ($isDistrict) {// 返回省市区+门店
            return [
                'shopSelect' => self::getSelectionDistrictCache()
            ];
        }
        return [
            "shopSelect" => json_decode(self::getSelectionCache($status, $openFyStatus), true)
        ];
    }

    /**
     * @throws InvalidArgumentException
     */
    public static function delSelectionCache()
    {
        Cache::store("redis")->delete("ShopSelectionAll");
        $statusGroup       = array_merge(array_keys(\App\Kuafu\Shop\Enum\CodeEnum::SHOP_STATUS_TEXT), [""]);
        $openFyStatusGroup = [0, 1];
        foreach ($openFyStatusGroup as $openFyStatus) {
            foreach ($statusGroup as $status) {
                $cacheKey = vsprintf("ShopSelectionAll:status:%s:openFyStatus:%s", [
                    $status,
                    $openFyStatus
                ]);
                Cache::store("redis")->delete($cacheKey);
            }
        }

        Cache::forget('getSelectionNew');
    }

    /**
     * @param string $status
     * @param int $openFyStatus
     * @return mixed
     */
    public static function getSelectionCache(string $status = "", int $openFyStatus = 0)
    {
        $cacheKey = vsprintf("ShopSelectionAll:status:%s:openFyStatus:%s", [
            $status,
            $openFyStatus
        ]);


        return Cache::store("redis")->rememberForever(
            $cacheKey,
            function () use ($status, $openFyStatus) {
                return Shop::query()->select(["id", "shopCode", "name", "city", "addressPrefix", "branch"])
                           ->when($openFyStatus > 0, function ($query) {
                               $query->whereHas("shopFuYouPaymentsRel");
                           })
                           ->when(
                               strlen($status) > 0 && in_array($status, array_keys(ShopEnum::SHOP_STATUS_TEXT)),
                               function ($query) use ($status) {
                                   $query->where("status", $status);
                               }
                           )->get()
                           ->map(function ($v) {
                               $city        = explode("-", $v->addressPrefix)[1] ?? "";
                               $v->branch   = $v->branch ? $v->branch : "未知分公司";
                               $v->city     = $v->addressPrefix ? $v->city : 0;
                               $v->cityText = $v->addressPrefix ? (empty($city) ? '未知市区' : $city) : "未知市区";
                               return $v;
                           })->groupBy("branch")->map(function ($v) {
                        return $v->groupBy("cityText");
                    })->map(function ($v, $k) {
                        return [
                            "name"     => $k,
                            "children" => $v->map(function (Collection $iv, $ik) {
                                return [
                                    "name"     => $ik,
                                    "children" => $iv->map(function (Shop $shopRow) {
                                        $shopRow->setAttribute("shopCode", str_pad(
                                            $shopRow->shopCode,
                                            ShopCodeEnum::CODE_TEXT[ShopCodeEnum::LEADING_ZERO],
                                            "0",
                                            STR_PAD_LEFT
                                        ));
                                        return $shopRow->only(["id", "name", "shopCode"]);
                                    })
                                ];
                            })->values()
                        ];
                    })->values()->toJson(JSON_UNESCAPED_UNICODE);
            }
        );
    }

    public static function isCollectShop(int $shopId, int $isCollect, int $userId)
    {
        $collectShopModel            = CollectShopListModel::firstOrNew([
            'shopId' => $shopId,
            'userId' => $userId,
        ]);
        $collectShopModel->isCollect = $isCollect;
        $collectShopModel->save();
    }

    public static function userShopLists(int $userId): array
    {
        // 获取当前用户有的店铺列表
        $shopList = ShopService::getShopListByUserAndPlatform($userId, CodeEnum::PLATFORM_BUSINESS, [
            'status' => [
                ShopEnum::SHOP_STATUS_NORMAL,
                ShopEnum::SHOP_STATUS_PREPARE
            ]
        ]);
        //获取用户收藏的门店列表
        $collectShopList = CollectShopListModel::query()
                                               ->where('userId', $userId)
                                               ->where('isCollect', CollectShopListModel::COLLECT_SHOP_TRUE)
                                               ->pluck('shopId')
                                               ->toArray();
        $shopCollect     = [];
        $shopListTemp    = [];
        collect($shopList)
            ->each(function ($shop) use ($collectShopList, &$shopCollect, &$shopListTemp) {
                if (in_array($shop['id'], $collectShopList)) {
                    $shopCollect[] = [
                        'id'        => (string)$shop['id'],
                        'isCollect' => CollectShopListModel::COLLECT_SHOP_TRUE,
                        'shopCode'  => CommonService::zeroFill($shop['shopCode']),
                        'name'      => $shop['name'],
                        'address'   => $shop['address'],
                        'prepare'   => (int)($shop['status'] == ShopEnum::SHOP_STATUS_PREPARE),
                    ];
                } else {
                    $shopListTemp[] = [
                        'id'        => (string)$shop['id'],
                        'isCollect' => CollectShopListModel::COLLECT_SHOP_FALSE,
                        'shopCode'  => CommonService::zeroFill($shop['shopCode']),
                        'name'      => $shop['name'],
                        'address'   => $shop['address'],
                        'prepare'   => (int)($shop['status'] == ShopEnum::SHOP_STATUS_PREPARE),
                    ];
                }
            });
        $shopCollect  = collect($shopCollect)->sortBy('shopCode')->toArray();
        $shopListTemp = collect($shopListTemp)->sortBy('shopCode')->toArray();
        return array_merge($shopCollect, $shopListTemp);
    }

    /**
     * 门店按照省的统计概览
     * @param array $provinceCodes
     * @param int $openStartTime
     * @param int $openEndTime
     * @return array
     */
    public static function overviewByProvinceCodes(array $provinceCodes, int $openStartTime, int $openEndTime)
    {
        // 在营门店数量
        $openingNum = Shop::query()
                          ->whereIn('province', $provinceCodes)
                          ->where('status', ShopEnum::SHOP_STATUS_NORMAL)
                          ->groupBy('province')
                          ->select(['province', DB::raw("count(*) as ct")])
                          ->get()
                          ->keyBy('province');

        // 本月新开数量
        $newOpenNum = Shop::query()
                          ->whereIn('province', $provinceCodes)
                          ->where('status', ShopEnum::SHOP_STATUS_NORMAL)
                          ->whereBetween('formalUseAt', [$openStartTime, $openEndTime])
                          ->groupBy('province')
                          ->select(['province', DB::raw("count(*) as ct")])
                          ->get()
                          ->keyBy('province');

        return collect($provinceCodes)->map(function ($provinceCode) use ($openingNum, $newOpenNum) {
            return [
                'provinceCode' => $provinceCode,
                'openingNum'   => $openingNum[$provinceCode]['ct'] ?? 0,
                'newOpenNum'   => $newOpenNum[$provinceCode]['ct'] ?? 0,
            ];
        })->toArray();
    }


    /**
     * 获取门店功能开通信息
     *
     * @param int $shopId
     * @return array
     */
    public static function getShopFunctionOpenDetail(int $shopId): array
    {
        $thirdOpenDetail  = WaiMaiShopMappingModel::query()->where("shopId", $shopId)->get();
        $kfCashOpenDetail = ShopPaymentsModel::query()
                                             ->where("shopId", $shopId)
                                             ->where("payChannel", PayStreamModel::PAY_CHANNEL_FY)
                                             ->where("isOpen", 1)
                                             ->first();

        // 获取设备序列号
        $serialNumber = (string)Shop::query()->where('id', $shopId)->value('serialNumber');

        return [
            "functionOpenDetail" => collect([
                WaiMaiDBEnum::SOURCE_TYPE_ELM,
                WaiMaiDBEnum::SOURCE_TYPE_DOUYIN,
                WaiMaiDBEnum::SOURCE_TYPE_MT_WM,
                WaiMaiDBEnum::SOURCE_TYPE_MT_TG
            ])->map(function ($v) use ($thirdOpenDetail) {
                $exist = $thirdOpenDetail->where("sourceType", $v)->first();
                switch ($v) {
                    case WaiMaiDBEnum::SOURCE_TYPE_ELM:
                        $key = "eleTakeaway";
                        break;
                    case WaiMaiDBEnum::SOURCE_TYPE_DOUYIN:
                        $key = "tiktokTakeaway";
                        break;
                    case WaiMaiDBEnum::SOURCE_TYPE_MT_WM:
                        $key = "mtTakeaway";
                        break;
                    case WaiMaiDBEnum::SOURCE_TYPE_MT_TG:
                        $key = "mtGroupBuy";
                        break;
                    default:
                        $key = null;
                        break;
                }
                return [
                    "type"   => $key,
                    "isOpen" => (bool)$exist,
                    "time"   => $exist ? $exist->createdAt->timestamp : 0
                ];
            })->values()->push([
                "type"   => "kfCash",
                "isOpen" => (bool)$kfCashOpenDetail,
                "serialNumber" => (bool)strlen($serialNumber),
                "time"   => $kfCashOpenDetail ? $kfCashOpenDetail->createdAt->timestamp : 0
            ])->toArray()
        ];
    }

    /**
     * 商家app专用 - 门店列表
     * @param int $uid
     * @param bool $isStaff
     * @param string $name
     * @param int $shopId
     * @param int $isCollected
     * @param int $page
     * @param int $pageSize
     * @return array
     * @throws BaseException
     * @throws GuzzleException
     * @throws ShopException
     * @throws UserException
     */
    public static function appShopList(
        int $uid,
        bool $isStaff,
        string $name,
        int $shopId,
        int $isCollected = 0,
        int $page = 1,
        int $pageSize = 10,
        array $selectColumns = [],
        array $whereCondition = []
    ): array {
        $selectColumns = $selectColumns ?: [
            'id',
            'shopCode',
            'name',
            'brandId',
            'imageUrl',
            'address',
            'type',
            'status',
            'updatedByUid',
            'createdByUid',
            'serialNumber',
        ];

        $shopPaginate = self::shopListDb($uid, $isStaff, $name, $shopId, $isCollected, $page, $pageSize, $selectColumns,
            $whereCondition, false);

        $shopIds = collect($shopPaginate->items())
            ->pluck('id')
            ->toArray();

        if ($shopIds) {

            // tiger辅助接口
            $subShopLists = TigerCurl::getIns()
                                     ->do('internal/shop/shopListsSub', ['shopIds' => $shopIds],
                                         'POST')['data'] ?? [];

            request()->renewOrders = collect(attributeGet($subShopLists, "renewOrders", []));
            // 开店状态
            $shopStepStatus = collect($subShopLists['nowStepStatus'] ?? [])->keyBy('shopId');

            // 整改单
            $against = $subShopLists['againstIds'] ?? [];

            $shopPaginates = collect($shopPaginate->items())->map(function ($item) use ($shopStepStatus, $against) {
                $item['nowStepStatus'] = $shopStepStatus[$item['id']]['nowStepStatus'] ?? 0;
                $item['against']       = $against[$item['id']] ?? [];

                return $item;
            });
        } else {
            request()->renewOrders = collect([]);
            $shopPaginates         = collect($shopPaginate->items());
        }


        return [
            "list"     => ShopListResource::collection($shopPaginates),
            "total"    => $shopPaginate->total(),
            "page"     => $shopPaginate->currentPage(),
            "pageSize" => $pageSize
        ];
    }

    /**
     * 用户下的门店列表sql
     * @param int $uid
     * @param bool $isStaff
     * @param string $name
     * @param int $shopId
     * @param int $isCollected
     * @param int $page
     * @param int $pageSize
     * @param array $selectColumns
     * @param array $whereCondition
     * @param bool $getArray
     * @param string $city
     * @param string $province
     * @param string $district
     * @param string $branch
     * @param bool $isKms
     * @param array $provinceCodes
     * @param array $cityCodes
     * @param array $areaCodes
     * @param array $shopIds
     */
    public static function shopListDb(
        int $uid,
        bool $isStaff,
        string $name,
        int $shopId,
        int $isCollected = 0,
        int $page = 1,
        int $pageSize = 10,
        array $selectColumns = [],
        array $whereCondition = [],
        bool $getArray = true,
        string $city = '',
        string $province = '',
        string $district = '',
        string $branch = '',
        bool $isKms = false,
        array $provinceCodes = [],
        array $cityCodes = [],
        array $areaCodes = [],
        array $shopIds = []
    ) {
        $selectColumns = $selectColumns ?: [
            'id',
            'shopCode',
            'name',
            'brandId',
            'imageUrl',
            'address',
            'type',
            'status',
            'updatedByUid',
            'createdByUid',
            'serialNumber',
        ];

        //获取用户收藏的门店列表
        ShopListResource::$collectShopIds = $collectShopIds = CollectShopListModel::query()
                                                                                  ->where('userId', $uid)
                                                                                  ->where('isCollect',
                                                                                      CollectShopListModel::COLLECT_SHOP_TRUE)
                                                                                  ->pluck('shopId')
                                                                                  ->toArray();

        $lists = Shop::query()
                     ->when(!$isStaff, function ($query) use ($uid) {
                         // 获得有登录权限的门店列表
                         //$permissionName = 'app.my.merchantLogin'; 100408
                         $permissionId = (int)optional(ConfigModel::getConfigByTypeKeyOne('merchant', 'loginPermission', 60))->value;
                         $query->whereRaw("id in (select shopId from kf_user_role_mappings where roleId in (select roleId from kf_role_permission_mappings where permissionId = {$permissionId}) and userId = {$uid})");
                     })
                     ->when(
                         $isStaff,
                         function ($query) use ($isKms, $uid) {
                             $query->when(!$isKms, function ($query) use ($uid) {

                                 if (in_array($uid,
                                     json_decode(ConfigModel::getConfigByTypeKeyOne('common', 'innerUids',
                                         60)->value))) {
                                     return $query;
                                 }

                                 $userOrganization = OrganizationManageRemote::organizationManageList($uid);
                                 if ($userOrganization["all"]) {
                                     return $query;
                                 } else {
                                     if (
                                         $userOrganization['shopIds'] ||
                                         $userOrganization["provinceCode"] ||
                                         $userOrganization["cityCode"] ||
                                         $userOrganization["districtCode"]
                                     ) {
                                         $subQueryWhere = function ($query) use ($userOrganization) {
                                             $query->when(
                                                 $userOrganization['shopIds'],
                                                 function ($query) use ($userOrganization) {
                                                     $query->orWhereIn('id', $userOrganization['shopIds']);
                                                 }
                                             )->when(
                                                 $userOrganization["provinceCode"],
                                                 function ($query) use ($userOrganization) {
                                                     $query->orWhereIn("province", $userOrganization["provinceCode"]);
                                                 }
                                             )->when(
                                                 $userOrganization["cityCode"],
                                                 function ($query) use ($userOrganization) {
                                                     $query->orWhereIn("city", $userOrganization["cityCode"]);
                                                 }
                                             )->when(
                                                 $userOrganization["districtCode"],
                                                 function ($query) use ($userOrganization) {
                                                     $query->orWhereIn("district", $userOrganization["districtCode"]);
                                                 }
                                             );
                                         };

                                         return $query->where($subQueryWhere);
                                     } else {
                                         throw new ShopException(ShopCodeEnum::ERR_NO_ACCESS_ERROR);
                                     }
                                 }
                             });
                         },
                         function ($query) use ($uid) {
                             $shopIdListIds = array_filter(array_unique(
                                 UserRoleMapping::getShopListByUserId($uid, CodeEnum::PLATFORM_BUSINESS)
                             ));
                             return $query->whereIn("id", $shopIdListIds);
                         })
                     ->when($isCollected > 0, function ($query) use ($collectShopIds) {
                         return $query->whereIn("id", $collectShopIds);
                     })
                     ->when(strlen($name) > 0, function ($query) use ($name) {
                         return $query->where(function ($query) use ($name) {
                             $shopCode = (int)$name;
                             $query    = $query->where("name", "like", "%{$name}%");
                             if ($shopCode > 0) {
                                 $query->orWhere("shopCode", "like", "%{$shopCode}%");
                             }
                         });
                     })
                     ->when($shopId !== 0, function ($query) use ($shopId) {
                         return $query->where("id", $shopId);
                     })
                     ->when(isset($whereCondition['status']) && $whereCondition['status'] != -1,
                         function ($query) use ($whereCondition) {
                             $query->where("status", $whereCondition['status']);
                         }, function ($query) {
                             $query->whereIn("status", [
                                 ShopEnum::SHOP_STATUS_NORMAL,
                                 ShopEnum::SHOP_STATUS_PREPARE
                             ]);
                         })
                     ->when($city !== '' && strlen($city) > 0, function ($query) use ($city) {
                         return $query->where("city", $city);
                     })
                     ->when($province !== '' && strlen($province) > 0, function ($query) use ($province) {
                         return $query->where("province", $province);
                     })
                     ->when($district !== '' && strlen($district) > 0, function ($query) use ($district) {
                         return $query->where("district", $district);
                     })
                     ->when($branch !== '' && strlen($branch) > 0, function ($query) use ($branch) {
                         return $query->where("branch", $branch);
                     })
                     ->when($provinceCodes, function ($query) use ($provinceCodes) {
                         return $query->whereIn("province", $provinceCodes);
                     })
                     ->when($cityCodes, function ($query) use ($cityCodes) {
                         return $query->whereIn("city", $cityCodes);
                     })
                     ->when($areaCodes, function ($query) use ($areaCodes) {
                         return $query->whereIn("district", $areaCodes);
                     })
                     ->when($shopIds, function ($query) use ($shopIds) {
                         return $query->whereIn('id', $shopIds);
                     })
                     ->when(in_array(config('app.env'), ['pre', 'prod']), function ($query) {
                         return $query->where("shopCode", "<", 100000);
                     })
                     ->orderBy("shopCode", "desc")
                     ->orderBy("id", "desc")
                     ->paginate($pageSize, $selectColumns, "page", $page);

        if ($getArray) {
            return $lists->items();
        }

        return $lists;
    }


    /**
     *
     * 更新门店地址信息
     *
     * @param object $params
     * @throws BaseException
     * @throws GuzzleException
     * @throws InvalidArgumentException
     */
    public static function synchronizeShopInfo(object $params)
    {
        try {
            self::updateShopInfo(['id' => $params->id], (array)$params);
        } catch (\Exception $exception) {
            handleException($exception, "选址同步门店地址相关信息失败");
        }
    }

    /**
     * @param object $params
     * @throws BaseException
     * @throws GuzzleException
     * @throws InvalidArgumentException
     */
    public static function switchShopOn(object $params)
    {
        try {
            self::updateShopInfo(['id' => $params->shopId], ["status" => ShopEnum::SHOP_STATUS_NORMAL]);
        } catch (\Exception $exception) {
            handleException($exception, "启用门店状态失败");
        }
    }

    /**
     * 闭店
     *
     * @param object $params
     * @throws BaseException
     * @throws GuzzleException
     * @throws InvalidArgumentException
     */
    public static function closeShop(object $params)
    {
        try {
            self::updateShopInfo(["shopCode" => $params->shopCode], ["status" => ShopEnum::SHOP_STATUS_BAN], 1, false);
        } catch (\Exception $exception) {
            handleException($exception, "闭店失败");
        }
    }

    /**
     * 修改门店手机号
     *
     * @param object $params
     * @throws BaseException
     * @throws GuzzleException
     * @throws InvalidArgumentException
     */
    public static function updateShopMobile(object $params)
    {
        try {
            $brandId   = 1;
            $shopModel = Shop::query()->where("shopCode", $params->shopCode)->first();
            if ($shopModel->shopMobile != $params->mobile) {
                $originalShopMobile = $shopModel->shopMobile;
                //手机号码发生更换
                self::updateShopInfo(["shopCode" => $params->shopCode], ["shopMobile" => $params->mobile], 1, false);

                // 配置的商家app店长角色id
                $managerRoleId = config('merchant.manager_role_id');
                $role          = Role::query()->find($managerRoleId);
                if (!$role || strpos($role->name, '店长') === false) {
                    $roleName = $role->name ?? '';
                    SendFs::newRobot()->setTitleContent('自动添加商家APP店长异常',
                        "role_id默认值异常，role_id:{$managerRoleId} name:{$roleName} shopId:{$shopModel->id}");
                }

                // 配置的收银pos店长角色id
                $posManagerRoleId = config('pos.manager_role_id');
                $posRole          = Role::query()->find($posManagerRoleId);
                if (!$posRole || strpos($posRole->name, '店长') === false) {
                    $posRoleName = $posRole->name ?? '';
                    SendFs::newRobot()->setTitleContent('自动添加pos店长异常',
                        "role_id默认值异常，role_id:{$posManagerRoleId} name:{$posRoleName} shopId:{$shopModel->id}");
                }

                // 判断手机号用户是否存在
                $user = User::withOutGlobalScope('isDel')->where('brandId', $brandId)->where('mobile', $params->mobile)
                            ->first();
                if ($user && $user->isDel == 1) {
                    $user->contact = $params->contact;
                    $user->isDel   = 0;
                    $user->save();
                }

                if ($user && $user->status != UserCodeEnum::USER_STATUS_USE) {
                    $user->status = UserCodeEnum::USER_STATUS_USE;
                    $user->save();
                }

                if (!$user) {
                    // 添加用户
                    $password = UserService::generateSimplePwd(8);
                    $user     = UserService::addUserDB($brandId, $params->contact, $params->mobile, $password);
                }


                //移除原手机号门店权限
                $originalUser = User::query()
                                    ->where('brandId', $brandId)
                                    ->where('mobile', $originalShopMobile)
                                    ->first();

                if ($originalUser) {
                    $originalUserShopRole = UserRoleMapping::query()
                                                           ->where("userId", $originalUser->id)
                                                           ->pluck("shopId");

                    UserRoleMapping::rmUserRole(
                        $originalUser->id, $shopModel->id, $managerRoleId, $brandId, RoleCodeEnum::ROLE_BUSINESS_TYPE
                    );
                    UserRoleMapping::rmUserRole(
                        $originalUser->id, $shopModel->id, $posManagerRoleId, $brandId, RoleCodeEnum::ROLE_POS_TYPE
                    );

                    if ($originalUserShopRole && count(array_unique($originalUserShopRole->toArray())) == 1) {
                        User::query()->where("id", $originalUser->id)->update([
                            "status" => UserCodeEnum::USER_STATUS_STOP
                        ]);
                    }
                }

                // 判断用户是否有商家店长权限
                if (!UserRoleService::checkRoleInShop($shopModel->id, $managerRoleId, RoleCodeEnum::ROLE_BUSINESS_TYPE,
                    $user->id)->count()) {

                    // 添加商家店长权限
                    UserRoleMapping::addUserRoleMap($user->id, $shopModel->id, $managerRoleId, $brandId,
                        RoleCodeEnum::ROLE_BUSINESS_TYPE);
                }

                // 判断用户是否有pos店长权限
                if (!UserRoleService::checkRoleInShop($shopModel->id, $posManagerRoleId, RoleCodeEnum::ROLE_POS_TYPE,
                    $user->id)->count()) {

                    // 添加商家店长权限
                    UserRoleMapping::addUserRoleMap($user->id, $shopModel->id, $posManagerRoleId, $brandId,
                        RoleCodeEnum::ROLE_POS_TYPE);
                }
            }

        } catch (\Exception $exception) {
            handleException($exception, "修改门店手机号失败");
        }
    }


    /**
     * 获取城市列表
     *
     * @param object $params
     * @return array|void
     * @throws BaseException
     */
    public static function cityShop(object $params)
    {
        try {
            return [
                "shopList" => CityShopListResource::collection(
                    Shop::query()
                        ->where("city", $params->cityCode)
                        ->when(!empty($params->name), function ($query) use ($params) {
                            $query->where('name', 'like', '%' . $params->name . '%')
                                  ->orWhere('shopCode', 'like', '%' . $params->name . '%');
                        })
                        ->where("lng", "<>", "")
                        ->where("lat", "<>", "")
                        ->where("status", "<>", ShopEnum::SHOP_STATUS_BAN)
                        ->where("shopCode", "<", 100000)
                        ->get()
                )
            ];
        } catch (\Exception $exception) {
            handleException($exception, "获取城市门店列表失败");
        }
    }

    /**
     * 模糊查询门店信息
     *
     * @param object $params
     * @return array|void
     * @throws BaseException
     */
    public static function getShopInfoByLikeSearch(object $params)
    {
        try {
            return [
                "shopInfo" => Shop::query()->when(
                    is_numeric($params->searchWhere),
                    function ($query) use ($params) {
                        $query->orWhere("shopCode", "like", "%$params->searchWhere%")
                              ->orWhere("id", "like", "%$params->searchWhere%");
                    },
                    function ($query) use ($params) {
                        $query->where("name", "like", "%$params->searchWhere%");
                    }
                )->when(!$params->withFields, function ($query) {
                    $query->select("id");
                })->get()->toArray()
            ];
        } catch (\Exception $exception) {
            handleException($exception, "获取城市门店列表失败");
        }
    }

    /**
     * 模糊查询门店信息
     *
     * @param object $params
     * @return array|void
     * @throws BaseException
     */
    public static function getShopListWithParams(object $params)
    {
        try {
            $list = Shop::query()
                        ->when(
                            !empty($params->searchWhere),
                            function ($query) use ($params) {
                                $query->orWhere("shopCode", "like", "%$params->searchWhere%")
                                      ->orWhere("id", "like", "%$params->searchWhere%")
                                      ->orWhere("name", "like", "%$params->searchWhere%");
                            }
                        )
                        ->when(!empty($params->withFields), function ($query) use ($params) {
                            $query->select(explode(',', $params->withFields));
                        })
                        ->when(!empty($params->lastId), function ($query) use ($params) {
                            $query->where('id', '>', $params->lastId);
                        })
                        ->limit(1000)
                        ->get()
                        ->toArray();

            return [
                "list" => $list
            ];
        } catch (\Exception $exception) {
            handleException($exception, "获取城市门店列表失败");
        }
    }

    /**
     * 根据门店id获取门店详细信息
     *
     * @param object $params
     * @return array|void
     * @throws BaseException
     */
    public static function getShopDetailByShopId(object $params)
    {
        try {
            $shopModel        = Shop::query()->where("id", $params->shopId)->first();
            $kfCashOpenDetail = ShopPaymentsModel::query()
                                                 ->where("shopId", $params->shopId)
                                                 ->where("payChannel", PayStreamModel::PAY_CHANNEL_FY)
                                                 ->where("isOpen", 1)
                                                 ->first();

            return [
                "shopDetail"       => $shopModel->toArray(),
                "fyOpenStatus"     => (bool)$kfCashOpenDetail,
                "cameraOpenStatus" => (bool)CameraModel::query()->where("shopId", $params->shopId)
                                                       ->where("deviceType", CameraModel::DEVICE_TYPE_CAMERA)->first(),
                "tvOpenStatus"     => (bool)CameraModel::query()->where("shopId", $params->shopId)
                                                       ->where("deviceType", CameraModel::DEVICE_TYPE_TV)->first(),
            ];

        } catch (\Exception $exception) {
            handleException($exception, "获取门店详细信息失败");
        }
    }

    public static function getMQTTAccountByDeviceSerialNo(string $serialNo): array
    {
        $deviceInfo = DeviceService::getDeviceRow($serialNo);

        if (empty($deviceInfo)) {
            throw new BaseException(BaseCodeEnum::ERR_SERVER_ERROR, '序列号不存在');
        }

        $shopId = $deviceInfo->shopId;
        $userId = 0;
        $result = [
            'shop' => MQTT::getAccountByConnectType($userId, $shopId, MQTT::MQTT_CLIENT_CONNECT_SHOP),
        ];
        return $result;
    }

    /**
     * 查询小红书门店列表
     *
     * @param object $params
     * @return array
     * @throws BaseException
     * @throws GuzzleException
     */
    public static function searchXhsShopList(object $params): array
    {
        try {
            $xhsShopList = XhsShopRemote::searchShop($params->search);
            return [
                "list" => $xhsShopList
            ];
        } catch (\Exception $exception) {
            handleException($exception, "查询小红书门店信息失败");
        }
    }

    /**
     * 绑定小红书门店
     *
     * @param object $params
     * @return void
     * @throws BaseException
     * @throws GuzzleException
     */
    public static function bindXhsShop(object $params)
    {
        try {
            $xhsShopDetail = XhsShopRemote::getShopDetail($params->poiId);
            if (empty($xhsShopDetail)) {
                throw new BaseException(BaseCodeEnum::ERR_SERVER_ERROR, '小红书门店信息查询不存在');
            }

            $bindExistsShopId = (int)WaiMaiShopMappingModel::query()
                                                           ->where('thirdShopId', $params->poiId)
                                                           ->where('sourceType', WaiMaiDBEnum::SOURCE_TYPE_XHS)
                                                           ->value('shopId');

            if ($bindExistsShopId) {
                throw new BaseException(BaseCodeEnum::ERR_SERVER_ERROR, "小红书门店已被门店【 $bindExistsShopId 】绑定");
            }

            WaiMaiShopMappingModel::addData(
                WaiMaiDBEnum::SOURCE_TYPE_XHS,
                $params->shopId,
                $params->poiId,
                $xhsShopDetail["poi_name"]
            );
        } catch (\Exception $exception) {
            handleException($exception, "小红书门店绑定失败");
        }
    }

    /**
     * 绑定小红书门店
     *
     * @param object $params
     * @return void
     * @throws BaseException
     */
    public static function unbindXhsShop(object $params)
    {
        try {
            $bindExists = WaiMaiShopMappingModel::query()
                                                ->where('shopId', $params->shopId)
                                                ->where('sourceType', WaiMaiDBEnum::SOURCE_TYPE_XHS)
                                                ->first();

            if (!$bindExists) {
                throw new BaseException(BaseCodeEnum::ERR_SERVER_ERROR, "小红书门店绑定信息不存在");
            }

            $bindExists->isDeleted = 1;
            $bindExists->save();
        } catch (\Exception $exception) {
            handleException($exception, "小红书门店解除绑定失败");
        }
    }


    /**
     * 获取附近门店列表
     *
     * @param $lng
     * @param $lat
     * @param int $platformId
     * @param string $orderId
     * @param string $poiId
     * @param int $limit
     * @return array[]
     */
    public static function getNearShopFromDy(
        $lng,
        $lat,
        int $platformId = PlatformCodeEnum::PLATFORM_APPLET_DOUYIN,
        string $orderId = '',
        string $poiId = '',
        string $skuId = '',
        int $limit = 10
    ): array {
        $keyArr = [
            'lng'        => $lng,
            'lat'        => $lat,
            'platformId' => $platformId,
            'orderId'    => $orderId,
            'poiId'      => $poiId,
            'skuId'      => $skuId
        ];
        return Cache::remember('getNearShopFromDy:' . md5(json_encode($keyArr)), getSwitchCacheTime(600),
            function () use ($lng, $lat, $platformId, $orderId, $poiId, $limit, $skuId) {
                $address = TencentMapHelp::getInstance()->inverseAddressResolution($lat, $lng);
                if ($address == false || empty($address['result']['address_component']['city'])) {
                    throw new ShopException(ShopCodeEnum::ERR_GET_ADDRESS);
                }
                $city   = $address['result']['address_component']['city'];
                $fields = [
                    'id',
                    'name',
                    'shopMobile',
                    'brandId',
                    'imageUrl',
                    'address',
                    'lng',
                    'lat',
                    'startAt',
                    'endAt',
                    'status',
                    'formalUseAt',
                    'isBusiness',
                    'appletStatus',
                    'dyCouponVerifySwitch'
                ];

                // 获取pos 抖音小程序 灰度省份配置 {"110000":"北京市","310000":"上海市"}
                $grayProvinceConfig = ConfigModel::getConfigByTypeKeyOne('applet', 'dyGrayProvinceConfig');
                $grayProvinceArray  = json_decode($grayProvinceConfig->value ?? '', true) ?? [];

                $shopIdList = [];
                if (!empty($skuId)) {
                    $poiList    = $this->getPoiIdListBySkuId($skuId);
                    $shopIdList = WaiMaiShopMappingModel::query()
                                                        ->select('shopId')
                                                        ->whereIn('thirdShopId', $poiList)
                                                        ->get()
                                                        ->pluck('shopId')
                                                        ->toArray();
                }

                $shopLists = Shop::query()
                                 ->where('addressPrefix', 'like', "%{$city}%")
                                 ->where('status', ShopEnum::SHOP_STATUS_NORMAL)
                                 ->select($fields)
                                 ->with('shopArchivesRel')
                                 ->when($platformId == PlatformCodeEnum::PLATFORM_APPLET_DOUYIN, function ($query) {
                                     $query->with(['waimaiShopMappingFromDouyin', 'shopFuYouPaymentsRel']);
                                 })
                                 ->when(count($shopIdList), function ($query) use ($shopIdList) {
                                     $query->whereIn('id', $shopIdList);
                                 })
                                 ->when($grayProvinceArray, function ($query) use ($grayProvinceArray) {
                                     $query->whereIn('province', array_keys($grayProvinceArray));
                                 })
                                 ->get();

                $nearShop = [];
                if ($shopLists) {
                    $nearShop = $shopLists
                        ->where("lng", ">", 0)
                        ->where("lat", ">", 0)
                        ->each(function ($v) use ($lat, $lng) {
                            $v->distance = CommonService::getDistance($lat, $lng, $v->lat, $v->lng) * 1000;
                            ShopService::resolveShopStatus($v);
                        })
                        ->sortBy('distance')
                        ->each(function ($item) {
                            $item['isOpenSelfDelivery'] = $item->shopArchivesRel->isOpenSelfDelivery ?? 0;
                            unset($item->shopArchivesRel);
                        })->map(function ($item) use ($poiId, $platformId) {
                            if ($platformId == PlatformCodeEnum::PLATFORM_APPLET_DOUYIN) {
                                if ($item->openStatus != ShopEnum::OPEN_STATUS_START) {// 营业状态
                                    return null;
                                }
                                $shopFuYouPaymentsId = $item->shopFuYouPaymentsRel->id ?? 0;
                                if (!$shopFuYouPaymentsId) {// 判断是否绑定富友 存在即为夸父收银
                                    return null;
                                }
                                unset($item->shopFuYouPaymentsRel);
                                $item['poiId'] = $item->waimaiShopMappingFromDouyin->thirdShopId ?? '';
                                unset($item->waimaiShopMappingFromDouyin);
                                if ($poiId) {
                                    if ($poiId == $item['poiId']) {
                                        return $item;
                                    }
                                } elseif ($item['poiId']) {
                                    return $item;
                                }
                            }
                        })
                        ->filter()
                        ->values()
                        ->toArray();
                }
                if ($orderId) {
                    if (count($nearShop) > 20) {
                        array_splice($nearShop, 20);
                    }
                    foreach ($nearShop as $k => $shop) {
//                        $canUse = false;
//                        try {
//                            $getOrderCanUse = (new DouyinSelfService)->getOrderCanUse($orderId, $shop['poiId']);
//                            $canUse         = $getOrderCanUse['can_use'] ?? false;
//                        } catch (\Exception $e) {
//                            \Illuminate\Support\Facades\Log::info('getOrderCanUse Err:', [
//                                'orderId' => $orderId,
//                                'poiId'   => $shop['poiId'],
//                                'msg'     => $e->getMessage(),
//                            ]);
//                        }
//                        if (!$canUse) {
//                            unset($nearShop[$k]);
//                        }
                    }
                }
                if (count($nearShop) > 10) {
                    array_splice($nearShop, 10);
                }
                return [
                    'nearShop' => $nearShop
                ];
            });
    }


    /**
     * 校验门店是否能正常下单
     *
     * @param int $shopId
     * @return void
     * @throws OrderException
     * @throws ShopException
     */
    public static function checkShopOrderStatus(int $shopId)
    {
        $shopInfo = ShopService::getShopAllStatus($shopId);
        if (empty($shopInfo)) {
            throw new ShopException(ShopCodeEnum::ERR_SHOP_ID_NO_EXIST);
        }
        // 判断营业状态
        if ($shopInfo['isBusiness'] == ShopEnum::SHOP_BUSINESS_STATUS_SUSPEND) {
            throw new OrderException(OrderCodeEnum::ERR_ORDER_SHOP_STATUS_IS_NOT);
        }

        //收银机状态判断
        if ($shopInfo['cashRegisterStatus'] != ShopEnum::SHOP_CASH_REGISTER_STATUS_OK) {
            throw new OrderException(OrderCodeEnum::ERR_ORDER_SHOP_CASH_REGISTER_STATUS_IS_NOT);
        }
    }

    /**
     * @param object $params
     * @throws BaseException
     */
    public static function dyCouponVerifySwitch(object $params)
    {
        try {
            Shop::query()->where("id", $params->shopId)->update([
                "dyCouponVerifySwitch" => $params->switch
            ]);
        } catch (\Exception $exception) {
            handleException($exception, "抖音券码核销开关修改失败");
        }
    }

    /**
     * 根据抖音侧的 SKUID 获取适用门店列表, 从 Redis 中获取
     *
     * @param string $skuId
     * @return array|mixed
     */
    public function getPoiIdListBySkuId(string $skuId)
    {
        try {
            $list = Redis::get(ShopCacheEnum::getDouyinProductShopListBySkuId($skuId));

            $list = json_decode($list, 1);

            return $list ?? [];
        } catch (\Exception $exception) {
            return [];
        }
    }

    public function getShopOrderStatus($skuId, $poiId, $orderId): array
    {
        $getOrderCanUse = (new DouyinSelfService)->getOrderCanUse($orderId, $poiId);;
        $canUse = $getOrderCanUse['can_use'] ?? false;

        return [
            'statusList' => [
                'dyCanUsedStatus'     => $canUse,
                'dyCanUsedStatusText' => $canUse ? '' : '券信息发生变更，重新购买后可在该门店核销~',
            ]
        ];
    }

    /**
     * 获取门店简称
     * 如:夸父炸串(西虹桥德必易园店),括号里(中英文括号)内的本
     * 如:华北区北京市望京凯德mall店,取值全名称
     * 进一步简化，不需要显示 省市 相关名称，需要优化名称中，江苏、江苏 去除 如：江苏苏州吴江汾湖店 取值  吴江汾湖店
     * @param array $info
     * @return string
     */
    public static function getShortName(array $info): string
    {
        $shortName = $info['shortName'] ?? '';
        $name      = $info['name'];
        if (mb_strlen($shortName)) {
            return $shortName;
        }
        if (mb_substr($name, -2, 2) == '的店') { // 使用个人名称的店 先不修改
            return $name;
        }
        $shopCode = str_pad($info['shopCode'],
            ShopCodeEnum::CODE_TEXT[ShopCodeEnum::LEADING_ZERO], '0', STR_PAD_LEFT);
        $name     = str_replace($shopCode, '', $name);// 简称 去掉编号

        if (mb_substr($name, 0, 5) == '夸父炸串（') {
            $name = mb_substr($name, 5);

            if (mb_substr($name, -1, 1) == '）') {
                $name = mb_substr($name, 0, mb_strlen($name) - 1);
            }
        } elseif (mb_substr($name, 0, 5) == '夸父炸串(') {
            $name = mb_substr($name, 5);

            if (mb_substr($name, -1, 1) == ')') {
                $name = mb_substr($name, 0, mb_strlen($name) - 1);
            }
        }

        $name = str_replace(['（', '）', '(', ')', '夸父'], '', $name);// 简称 去掉括号

        // 华北区、华东区、华南区、华中区,西北区
        if (mb_substr($name, 0, 3) == '华北区' || mb_substr($name, 0, 3) == '华东区' ||
            mb_substr($name, 0, 3) == '华南区' || mb_substr($name, 0, 3) == '华中区' ||
            mb_substr($name, 0, 3) == '西北区') {
            $name = mb_substr($name, 3);
        }
        if (mb_substr($name, 0, 4) == '华北一区' || mb_substr($name, 0, 4) == '华北二区' ||
            mb_substr($name, 0, 4) == '华北地区') {
            $name = mb_substr($name, 4);
        }

        // 匹配省市区
        $addressPrefix = $info['addressPrefix'];
        if ($addressPrefix && $addressPrefix != '--') {
            $addressPrefixData = explode('-', $addressPrefix);
            $province          = $addressPrefixData[0] ?? '';
            $city              = $addressPrefixData[1] ?? '';
            $district          = $addressPrefixData[2] ?? '';
            if ($province && $city && $district) {
                $provinceShortName = str_replace([
                    '省',
                    '市',
                    '回族自治区',
                    '壮族自治区',
                    '维吾尔自治区',
                    '特别行政区',
                    '自治区',
                ], '', $province);// 简称 去掉省 市 自治区
                if (mb_substr($name, 0, mb_strlen($province)) == $province) {
                    $name = mb_substr($name, mb_strlen($province));
                } elseif (mb_substr($name, 0, mb_strlen($provinceShortName)) == $provinceShortName) {
                    $name = mb_substr($name, mb_strlen($provinceShortName));
                }

                $cityShortName = str_replace([
                    '新区',
                    '地区',
                    '自治区',
                    '自治州',
                    '自治县',
                    '区',
                    '市',
                    '县',
                    '盟',
                ], '', $city);// 简称 去掉省 市 自治区
                if (mb_substr($name, 0, mb_strlen($city)) == $city) {
                    $name = mb_substr($name, mb_strlen($city));
                } elseif (mb_substr($name, 0, mb_strlen($cityShortName)) == $cityShortName) {
                    $name = mb_substr($name, mb_strlen($cityShortName));
                }

                if (in_array($provinceShortName, ['北京', '上海', '天津', '重庆'])) {
                    $districtShortName = str_replace([
                        '新区',
                        '地区',
                        '自治区',
                        '自治州',
                        '自治县',
                        '区',
                        '市',
                        '县',
                        '盟',
                    ], '', $district);// 简称 去掉省 市 自治区
                    if (mb_substr($name, 0, mb_strlen($district)) == $district) {
                        $name = mb_substr($name, mb_strlen($district));
                    } elseif (mb_substr($name, 0, mb_strlen($districtShortName)) == $districtShortName) {
                        $name = mb_substr($name, mb_strlen($districtShortName));
                    }
                }
            }
        }

        // 简称入库
        self::updateShopInfo(['id' => $info['id']], ['shortName' => $name]);

        return $name;
    }

    /**
     * 存在终端号时验证是否已经绑定了其他门店
     * @param array $params
     * @param int $shopId
     * @param int $payChannel
     * @throws \Exception
     */
    public static function checkReservedFyTermId(array $params, int $shopId, int $payChannel)
    {
        $reservedFyTermId = json_decode($params['payConfig'], true)['reserved_fy_term_id'] ?? '';
        if ($reservedFyTermId) {
            $existShopIds = ShopPaymentsModel::query()
                                             ->where('shopId', '!=', $shopId)
                                             ->where('payChannel', $payChannel)
                                             ->whereRaw(DB::raw("JSON_CONTAINS(payConfig, JSON_OBJECT('reserved_fy_term_id', '{$reservedFyTermId}'))"))
                                             ->get()
                                             ->pluck('shopId')
                                             ->toArray();
            if ($existShopIds) {
                throw new \Exception(
                    sprintf(
                        '终端号 [ %d ] 已存在其他门店id [ %s ]，请勿重复添加',
                        $reservedFyTermId,
                        implode(',', $existShopIds)
                    )
                );
            }
        }
    }

    /**
     * 修改门店信息-单个字段
     * @param int $shopId
     * @param array $data
     * @throws GuzzleException
     * @throws InvalidArgumentException
     * @throws ShopException
     */
    public static function updateField(int $shopId, array $data)
    {
        try {
            self::updateShopInfo(['id' => $shopId], $data);
        } catch (\Exception $exception) {
            throw new ShopException(ShopCodeEnum::ERR_SHOP_UPDATE,
                ShopCodeEnum::CODE_TEXT[ShopCodeEnum::ERR_SHOP_UPDATE], $exception);
        }
    }

    /**
     * 返回分公司-城市
     * @return mixed
     */
    public static function getSelectionOnlyCityCache()
    {
        return Cache::remember('getSelectionNew', getSwitchCacheTime(1200),
            function () {
                $shopSelect = [];
                Shop::query()->select(['id', 'city', 'addressPrefix', 'branch'])
                    ->groupBy('city')
                    ->get()
                    ->map(function ($v) use (&$shopSelect) {
                        if ($v->addressPrefix && $v->city) {
                            $city     = explode('-', $v->addressPrefix)[1] ?? '';
                            $branch   = $v->branch ? $v->branch : '未知分公司';
                            $cityCode = $v->addressPrefix ? $v->city : 0;
                            $cityText = $v->addressPrefix ? (empty($city) ? '未知市区' : $city) : '未知市区';
                            if (!isset($shopSelect[$branch])) {
                                $shopSelect[$branch] = [
                                    'name' => $branch,
                                ];
                            }
                            $shopSelect[$branch]['children'][] = [
                                'cityCode' => $cityCode,
                                'name'     => $cityText,
                            ];
                        }
                    });
                return array_values($shopSelect);
            }
        );
    }

    /**
     * 返回省市区+门店
     * @return mixed
     */
    public static function getSelectionDistrictCache()
    {
        return Cache::remember('getSelectionDistrict', getSwitchCacheTime(1200),
            function () {

                // 获取省市区三级
                $amapAdministrationDistrict = Cache::remember('getAmapAdministrationDistrict', 86400,
                    function () {
                        $url = ConfigService::getConfigDetail('rmsArea',
                                'amapAdministrationDistrict')['value'] ?? 'https://file.51kuafu.com/df/59/5f812ec6b2d4ccefb25fbfe9bb034878bc6bd693ff08cdc853e479915533.json';
                        return json_decode(file_get_contents($url),
                            JSON_UNESCAPED_UNICODE);
                    }
                );

                $shopList   = [];
                $getShopIds = OpenShopRemote::getShops() ?? [];// 获取门店开店状态在确认建店之后的门店数据
                Shop::query()
                    ->whereIn('id', $getShopIds)
                    ->whereIn('status', [ShopEnum::SHOP_STATUS_NORMAL, ShopEnum::SHOP_STATUS_PREPARE])
                    ->select(['id', 'shopCode', 'name', 'district'])
                    ->get()
                    ->map(function ($item) use (&$shopList) {
                        $shopList[$item->district][] = [
                            'id'       => $item->id,
                            'name'     => $item->name,
                            'shopCode' => $item->shopCode,
                        ];
                    });
                foreach ($amapAdministrationDistrict as &$province) {
                    foreach ($province['children'] as &$city) {
                        foreach ($city['children'] as &$district) {
                            $district['children'] = $shopList[$district['value']] ?? [];
                        }
                    }
                }
                return $amapAdministrationDistrict;
            }
        );
    }
}
